am 6f60bfda: am 0f279346: am a0a3f3f6: am 208e9088: Merge "Run Virtualizer test only when effect is available" into lmp-dev

* commit '6f60bfdac2f1ac4ef8160e31c7a007e73dff2512':
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 22540b8..e1541a1 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -195,12 +195,20 @@
     set_filter_off_or_fast_if_possible(props, req,
         "android.colorCorrection.availableAberrationModes",
         "android.colorCorrection.aberrationMode")
-    set_filter_off_or_fast_if_possible(props, req,
-        "android.hotPixel.availableHotPixelModes",
-        "android.hotPixel.mode")
-    set_filter_off_or_fast_if_possible(props, req,
-        "android.edge.availableEdgeModes",
-        "android.edge.mode")
+    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 90662db..a33a9f8 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/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..ae62f84 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_r1">
 
     <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/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 bb5527f..92eef80 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);
+                            createSession(outputSurfaces);
+
+                            // Warm up camera preview (mainly to give legacy devices time to do 3A).
+                            CaptureRequest.Builder warmupRequest =
+                                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                            warmupRequest.addTarget(yuvSurface);
+                            assertNotNull("Fail to get CaptureRequest.Builder", warmupRequest);
+                            SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+                            for (int i = 0; i < warmupCaptureNumber; i++) {
+                                startCapture(warmupRequest.build(), /*repeating*/false,
+                                        resultListener, mHandler);
+                            }
+                            for (int i = 0; i < warmupCaptureNumber; i++) {
+                                resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+                                Image image = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+                                image.close();
+                            }
+
+                            // Capture image.
+                            CaptureRequest.Builder mainRequest =
+                                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                            for (Surface s : outputSurfaces) {
+                                mainRequest.addTarget(s);
+                            }
+
+                            startCapture(mainRequest.build(), /*repeating*/false, resultListener,
+                                    mHandler);
+
+                            // Verify capture result and images
+                            resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+
+                            Image yuvImage = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+                            Image jpegImage = jpegListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+
+                            //Validate captured images.
+                            CameraTestUtils.validateImage(yuvImage, captureSz.getWidth(),
+                                    captureSz.getHeight(), format, /*filePath*/null);
+                            CameraTestUtils.validateImage(jpegImage, maxJpegSize.getWidth(),
+                                    maxJpegSize.getHeight(), ImageFormat.JPEG, /*filePath*/null);
+
+                            // Compare the image centers.
+                            RectF jpegDimens = new RectF(0, 0, jpegImage.getWidth(),
+                                    jpegImage.getHeight());
+                            RectF yuvDimens = new RectF(0, 0, yuvImage.getWidth(),
+                                    yuvImage.getHeight());
+
+                            // Find scale difference between YUV and JPEG output
+                            Matrix m = new Matrix();
+                            m.setRectToRect(yuvDimens, jpegDimens, Matrix.ScaleToFit.START);
+                            RectF scaledYuv = new RectF();
+                            m.mapRect(scaledYuv, yuvDimens);
+                            float scale = scaledYuv.width() / yuvDimens.width();
+
+                            final int PATCH_DIMEN = 40; // pixels in YUV
+
+                            // Find matching square patch of pixels in YUV and JPEG output
+                            RectF tempPatch = new RectF(0, 0, PATCH_DIMEN, PATCH_DIMEN);
+                            tempPatch.offset(yuvDimens.centerX() - tempPatch.centerX(),
+                                    yuvDimens.centerY() - tempPatch.centerY());
+                            Rect yuvPatch = new Rect();
+                            tempPatch.roundOut(yuvPatch);
+
+                            tempPatch.set(0, 0, PATCH_DIMEN * scale, PATCH_DIMEN * scale);
+                            tempPatch.offset(jpegDimens.centerX() - tempPatch.centerX(),
+                                    jpegDimens.centerY() - tempPatch.centerY());
+                            Rect jpegPatch = new Rect();
+                            tempPatch.roundOut(jpegPatch);
+
+                            // Decode center patches
+                            int[] yuvColors = convertPixelYuvToRgba(yuvPatch.width(),
+                                    yuvPatch.height(), yuvPatch.left, yuvPatch.top, yuvImage);
+                            Bitmap yuvBmap = Bitmap.createBitmap(yuvColors, yuvPatch.width(),
+                                    yuvPatch.height(), Bitmap.Config.ARGB_8888);
+
+                            byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpegImage);
+                            BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
+                                    compressedJpegData, /*offset*/0, compressedJpegData.length,
+                                    /*isShareable*/true);
+                            BitmapFactory.Options opt = new BitmapFactory.Options();
+                            opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                            Bitmap fullSizeJpegBmap = decoder.decodeRegion(jpegPatch, opt);
+                            Bitmap jpegBmap = Bitmap.createScaledBitmap(fullSizeJpegBmap,
+                                    yuvPatch.width(), yuvPatch.height(), /*filter*/true);
+
+                            // Compare two patches using average of per-pixel differences
+                            double difference = BitmapUtils.calcDifferenceMetric(yuvBmap, jpegBmap);
+
+                            Log.i(TAG, "Difference for resolution " + captureSz + " is: " +
+                                    difference);
+                            if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
+                                // Dump files if running in verbose mode
+                                if (DEBUG) {
+                                    String jpegFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+                                            "_jpeg.jpg";
+                                    dumpFile(jpegFileName, jpegBmap);
+                                    String fullSizeJpegFileName = DEBUG_FILE_NAME_BASE + "/" +
+                                            captureSz + "_full_jpeg.jpg";
+                                    dumpFile(fullSizeJpegFileName, compressedJpegData);
+                                    String yuvFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+                                            "_yuv.jpg";
+                                    dumpFile(yuvFileName, yuvBmap);
+                                    String fullSizeYuvFileName = DEBUG_FILE_NAME_BASE + "/" +
+                                            captureSz + "_full_yuv.jpg";
+                                    int[] fullYUVColors = convertPixelYuvToRgba(yuvImage.getWidth(),
+                                            yuvImage.getHeight(), 0, 0, yuvImage);
+                                    Bitmap fullYUVBmap = Bitmap.createBitmap(fullYUVColors,
+                                            yuvImage.getWidth(), yuvImage.getHeight(),
+                                            Bitmap.Config.ARGB_8888);
+                                    dumpFile(fullSizeYuvFileName, fullYUVBmap);
+                                }
+                                fail("Camera " + mCamera.getId() + ": YUV and JPEG image at " +
+                                        "capture size " + captureSz + " for the same frame are " +
+                                        "not similar, center patches have difference metric of " +
+                                        difference);
+                            }
+
+                            // Stop capture, delete the streams.
+                            stopCapture(/*fast*/false);
+                        } finally {
+                            closeImageReader(jpegReader);
+                            jpegReader = null;
+                            closeImageReader(yuvReader);
+                            yuvReader = null;
+                        }
+                    }
+                }
+
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+    /**
+     * Convert a rectangular patch in a YUV image to an ARGB color array.
+     *
+     * @param w width of the patch.
+     * @param h height of the patch.
+     * @param wOffset offset of the left side of the patch.
+     * @param hOffset offset of the top of the patch.
+     * @param yuvImage a YUV image to select a patch from.
+     * @return the image patch converted to RGB as an ARGB color array.
+     */
+    private static int[] convertPixelYuvToRgba(int w, int h, int wOffset, int hOffset,
+                                               Image yuvImage) {
+        final int CHANNELS = 3; // yuv
+        final float COLOR_RANGE = 255f;
+
+        assertTrue("Invalid argument to convertPixelYuvToRgba",
+                w > 0 && h > 0 && wOffset >= 0 && hOffset >= 0);
+        assertNotNull(yuvImage);
+
+        int imageFormat = yuvImage.getFormat();
+        assertTrue("YUV image must have YUV-type format",
+                imageFormat == ImageFormat.YUV_420_888 || imageFormat == ImageFormat.YV12 ||
+                        imageFormat == ImageFormat.NV21);
+
+        int height = yuvImage.getHeight();
+        int width = yuvImage.getWidth();
+
+        Rect imageBounds = new Rect(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
+        Rect crop = new Rect(/*left*/wOffset, /*top*/hOffset, /*right*/wOffset + w,
+                /*bottom*/hOffset + h);
+        assertTrue("Output rectangle" + crop + " must lie within image bounds " + imageBounds,
+                imageBounds.contains(crop));
+        Image.Plane[] planes = yuvImage.getPlanes();
+
+        Image.Plane yPlane = planes[0];
+        Image.Plane cbPlane = planes[1];
+        Image.Plane crPlane = planes[2];
+
+        ByteBuffer yBuf = yPlane.getBuffer();
+        int yPixStride = yPlane.getPixelStride();
+        int yRowStride = yPlane.getRowStride();
+        ByteBuffer cbBuf = cbPlane.getBuffer();
+        int cbPixStride = cbPlane.getPixelStride();
+        int cbRowStride = cbPlane.getRowStride();
+        ByteBuffer crBuf = crPlane.getBuffer();
+        int crPixStride = crPlane.getPixelStride();
+        int crRowStride = crPlane.getRowStride();
+
+        int[] output = new int[w * h];
+
+        // TODO: Optimize this with renderscript intrinsics
+        byte[] yRow = new byte[yPixStride * w];
+        byte[] cbRow = new byte[cbPixStride * w / 2];
+        byte[] crRow = new byte[crPixStride * w / 2];
+        yBuf.mark();
+        cbBuf.mark();
+        crBuf.mark();
+        int initialYPos = yBuf.position();
+        int initialCbPos = cbBuf.position();
+        int initialCrPos = crBuf.position();
+        int outputPos = 0;
+        for (int i = hOffset; i < hOffset + h; i++) {
+            yBuf.position(initialYPos + i * yRowStride + wOffset * yPixStride);
+            yBuf.get(yRow);
+            if ((i & 1) == (hOffset & 1)) {
+                cbBuf.position(initialCbPos + (i / 2) * cbRowStride + wOffset * cbPixStride / 2);
+                cbBuf.get(cbRow);
+                crBuf.position(initialCrPos + (i / 2) * crRowStride + wOffset * crPixStride / 2);
+                crBuf.get(crRow);
+            }
+            for (int j = 0, yPix = 0, crPix = 0, cbPix = 0; j < w; j++, yPix += yPixStride) {
+                float y = yRow[yPix] & 0xFF;
+                float cb = cbRow[cbPix] & 0xFF;
+                float cr = crRow[crPix] & 0xFF;
+
+                // convert YUV -> RGB (from JFIF's "Conversion to and from RGB" section)
+                int r = (int) Math.max(0.0f, Math.min(COLOR_RANGE, y + 1.402f * (cr - 128)));
+                int g = (int) Math.max(0.0f,
+                        Math.min(COLOR_RANGE, y - 0.34414f * (cb - 128) - 0.71414f * (cr - 128)));
+                int b = (int) Math.max(0.0f, Math.min(COLOR_RANGE, y + 1.772f * (cb - 128)));
+
+                // Convert to ARGB pixel color (use opaque alpha)
+                output[outputPos++] = Color.rgb(r, g, b);
+
+                if ((j & 1) == 1) {
+                    crPix += crPixStride;
+                    cbPix += cbPixStride;
+                }
+            }
+        }
+        yBuf.rewind();
+        cbBuf.rewind();
+        crBuf.rewind();
+
+        return output;
+    }
 
     /**
      * Test capture a given format stream with yuv stream simultaneously.
@@ -270,6 +595,36 @@
         }
     }
 
+    /**
+     * Test buffer access after release, YUV420_888 single capture is tested. This method
+     * should throw ISE.
+     */
+    private void bufferAccessAfterRelease() throws Exception {
+        final int FORMAT = ImageFormat.YUV_420_888;
+        Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(FORMAT,
+                StaticMetadata.StreamDirection.Output);
+
+        try {
+            // Create ImageReader.
+            mListener = new SimpleImageListener();
+            createDefaultImageReader(availableSizes[0], FORMAT, MAX_NUM_IMAGES, mListener);
+
+            // Start capture.
+            CaptureRequest request = prepareCaptureRequest();
+            SimpleCaptureCallback listener = new SimpleCaptureCallback();
+            startCapture(request, /* repeating */false, listener, mHandler);
+
+            mListener.waitForAnyImageAvailable(CAPTURE_WAIT_TIMEOUT_MS);
+            Image img = mReader.acquireNextImage();
+            ByteBuffer buffer = img.getPlanes()[0].getBuffer();
+            img.close();
+
+            byte data = buffer.get(); // An ISE should be thrown here.
+        } finally {
+            closeDefaultImageReader();
+        }
+    }
+
     private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
 
         Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
index 8b8f2f6..e669007 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -195,11 +195,6 @@
             try {
                 openDevice(id);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
-
                 boolean partialsExpected = mStaticInfo.getPartialResultCount() > 1;
                 long startTimeMs;
                 boolean isPartialTimingValid = partialsExpected;
@@ -216,8 +211,8 @@
                             new SimpleTimingResultListener();
                     SimpleImageListener imageListener = new SimpleImageListener();
 
-                    Size maxYuvSize = CameraTestUtils.getSupportedPreviewSizes(id, mCameraManager,
-                            /*bound*/null).get(0);
+                    Size maxYuvSize = CameraTestUtils.getSortedSizesForFormat(
+                        id, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null).get(0);
 
                     prepareCaptureAndStartPreview(previewBuilder, captureBuilder,
                             mOrderedPreviewSizes.get(0), maxYuvSize,
@@ -257,7 +252,7 @@
                     // simulate real scenario (preview runs a bit)
                     waitForNumResults(previewResultListener, NUM_RESULTS_WAIT);
 
-                    stopPreview();
+                    blockingStopPreview();
 
                 }
                 mReportLog.printArray("Camera " + id
@@ -335,7 +330,9 @@
      */
     private void initializeImageReader(String cameraId, int format) throws Exception {
         mOrderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
-                cameraId, mCameraManager, CameraTestUtils.PREVIEW_SIZE_BOUND);
+                cameraId, mCameraManager,
+                CameraTestUtils.getPreviewSizeBound(mWindowManager,
+                    CameraTestUtils.PREVIEW_SIZE_BOUND));
         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
         createImageReader(maxPreviewSize, format, NUM_MAX_IMAGES, /*listener*/null);
         updatePreviewSurface(maxPreviewSize);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index 21920b7..20a7834 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -30,8 +30,8 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.MediaCodecList;
+import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.media.MediaPlayer;
 import android.media.MediaRecorder;
 import android.os.Environment;
 import android.os.SystemClock;
@@ -66,15 +66,15 @@
     private static final int VIDEO_FRAME_RATE = 30;
     private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
     private static final int[] mCamcorderProfileList = {
+            CamcorderProfile.QUALITY_HIGH,
             CamcorderProfile.QUALITY_2160P,
             CamcorderProfile.QUALITY_1080P,
-            CamcorderProfile.QUALITY_480P,
             CamcorderProfile.QUALITY_720P,
+            CamcorderProfile.QUALITY_480P,
             CamcorderProfile.QUALITY_CIF,
-            CamcorderProfile.QUALITY_LOW,
-            CamcorderProfile.QUALITY_HIGH,
             CamcorderProfile.QUALITY_QCIF,
             CamcorderProfile.QUALITY_QVGA,
+            CamcorderProfile.QUALITY_LOW,
     };
     private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
     private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
@@ -86,6 +86,7 @@
     private String mOutMediaFileName;
     private int mVideoFrameRate;
     private Size mVideoSize;
+    private long mRecordingStartTime;
 
     @Override
     protected void setUp() throws Exception {
@@ -119,7 +120,7 @@
 
                 initSupportedVideoSize(mCameraIds[i]);
 
-                basicRecordingTestByCamera();
+                basicRecordingTestByCamera(mCamcorderProfileList);
             } finally {
                 closeDevice();
                 releaseRecorder();
@@ -222,6 +223,53 @@
     }
 
     /**
+     * <p>
+     * Test recording framerate accuracy when switching from low FPS to high FPS.
+     * </p>
+     * <p>
+     * This test first record a video with profile of lowest framerate then record a video with
+     * profile of highest framerate. Make sure that the video framerate are still accurate.
+     * </p>
+     */
+    public void testRecordingFramerateLowToHigh() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+
+                initSupportedVideoSize(mCameraIds[i]);
+
+                int minFpsProfileId = -1, minFps = 1000;
+                int maxFpsProfileId = -1, maxFps = 0;
+                int cameraId = Integer.valueOf(mCamera.getId());
+
+                for (int profileId : mCamcorderProfileList) {
+                    if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
+                        continue;
+                    }
+                    CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+                    if (profile.videoFrameRate < minFps) {
+                        minFpsProfileId = profileId;
+                        minFps = profile.videoFrameRate;
+                    }
+                    if (profile.videoFrameRate > maxFps) {
+                        maxFpsProfileId = profileId;
+                        maxFps = profile.videoFrameRate;
+                    }
+                }
+
+                int camcorderProfileList[] = new int[] {minFpsProfileId, maxFpsProfileId};
+                basicRecordingTestByCamera(camcorderProfileList);
+            } finally {
+                closeDevice();
+                releaseRecorder();
+            }
+        }
+    }
+
+    /**
      * Test slow motion recording where capture rate (camera output) is different with
      * video (playback) frame rate for each camera if high speed recording is supported
      * by both camera and encoder.
@@ -288,8 +336,9 @@
                     updatePreviewSurfaceWithVideoSize(size);
 
                     // Start recording
+                    SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
                     startSlowMotionRecording(/*useMediaRecorder*/true, videoFramerate, captureRate,
-                            fpsRange);
+                            fpsRange, resultListener);
                     long startTime = SystemClock.elapsedRealtime();
 
                     // Record certain duration.
@@ -297,10 +346,12 @@
 
                     // Stop recording and preview
                     stopRecording(/*useMediaRecorder*/true);
-                    int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+                    // Convert number of frames camera produced into the duration in unit of ms.
+                    int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+                                    videoFramerate);
 
                     // Validation.
-                    validateRecording(size, duration * SLOWMO_SLOW_FACTOR);
+                    validateRecording(size, durationMs);
 
                 }
 
@@ -317,7 +368,7 @@
         Range<Integer> maxRange = availableFpsRanges[0];
         boolean foundRange = false;
         for (Range<Integer> range : availableFpsRanges) {
-            if (range.getLower() == range.getUpper() && range.getLower() >= maxRange.getLower()) {
+            if (range.getLower().equals(range.getUpper()) && range.getLower() >= maxRange.getLower()) {
                 foundRange = true;
                 maxRange = range;
             }
@@ -330,7 +381,8 @@
     }
 
     private void startSlowMotionRecording(boolean useMediaRecorder, int videoFrameRate,
-            int captureRate, Range<Integer> fpsRange) throws Exception {
+            int captureRate, Range<Integer> fpsRange,
+            CameraCaptureSession.CaptureCallback listener) throws Exception {
         List<Surface> outputSurfaces = new ArrayList<Surface>(2);
         assertTrue("Both preview and recording surfaces should be valid",
                 mPreviewSurface.isValid() && mRecordingSurface.isValid());
@@ -371,7 +423,7 @@
         for (int i = 0; i < slowMotionFactor - 1; i++) {
             slowMoRequests.add(recordingOnlyBuilder.build()); // Recording only.
         }
-        mSession.setRepeatingBurst(slowMoRequests, null, null);
+        mSession.setRepeatingBurst(slowMoRequests, listener, mHandler);
 
         if (useMediaRecorder) {
             mMediaRecorder.start();
@@ -385,9 +437,9 @@
      * Test camera recording by using each available CamcorderProfile for a
      * given camera. preview size is set to the video size.
      */
-    private void basicRecordingTestByCamera() throws Exception {
+    private void basicRecordingTestByCamera(int[] camcorderProfileList) throws Exception {
         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
-        for (int profileId : mCamcorderProfileList) {
+        for (int profileId : camcorderProfileList) {
             int cameraId = Integer.valueOf(mCamera.getId());
             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
                     allowedUnsupported(cameraId, profileId)) {
@@ -423,18 +475,25 @@
             updatePreviewSurfaceWithVideoSize(videoSz);
 
             // Start recording
-            startRecording(/* useMediaRecorder */true);
-            long startTime = SystemClock.elapsedRealtime();
+            SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+            startRecording(/* useMediaRecorder */true, resultListener);
 
             // Record certain duration.
             SystemClock.sleep(RECORDING_DURATION_MS);
 
             // Stop recording and preview
             stopRecording(/* useMediaRecorder */true);
-            int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+            // Convert number of frames camera produced into the duration in unit of ms.
+            int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+                            profile.videoFrameRate);
+
+            if (VERBOSE) {
+                Log.v(TAG, "video frame rate: " + profile.videoFrameRate +
+                                ", num of frames produced: " + resultListener.getTotalNumFrames());
+            }
 
             // Validation.
-            validateRecording(videoSz, duration);
+            validateRecording(videoSz, durationMs);
         }
     }
 
@@ -466,18 +525,20 @@
             updatePreviewSurfaceWithVideoSize(sz);
 
             // Start recording
-            startRecording(/* useMediaRecorder */true);
-            long startTime = SystemClock.elapsedRealtime();
+            SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+            startRecording(/* useMediaRecorder */true, resultListener);
 
             // Record certain duration.
             SystemClock.sleep(RECORDING_DURATION_MS);
 
             // Stop recording and preview
             stopRecording(/* useMediaRecorder */true);
-            int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+            // Convert number of frames camera produced into the duration in unit of ms.
+            int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+                            VIDEO_FRAME_RATE);
 
             // Validation.
-            validateRecording(sz, duration);
+            validateRecording(sz, durationMs);
         }
     }
 
@@ -505,11 +566,6 @@
 
                     openDevice(id);
 
-                    if (mStaticInfo.isHardwareLevelLegacy()) {
-                        Log.i(TAG, "Skipping test on legacy devices");
-                        continue;
-                    }
-
                     initSupportedVideoSize(id);
 
                     videoSnapshotTestByCamera(burstTest);
@@ -558,6 +614,10 @@
             throws Exception {
         final int NUM_SINGLE_SHOT_TEST = 5;
         final int FRAMEDROP_TOLERANCE = 8;
+        final int FRAME_SIZE_15M = 15000000;
+        final float FRAME_DROP_TOLERENCE_FACTOR = 1.5f;
+        int kFrameDrop_Tolerence = FRAMEDROP_TOLERANCE;
+
         for (int profileId : mCamcorderProfileList) {
             int cameraId = Integer.valueOf(mCamera.getId());
             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
@@ -567,14 +627,21 @@
 
             CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
             Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+            Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+
+            if (mStaticInfo.isHardwareLevelLegacy() &&
+                    (videoSz.getWidth() > maxPreviewSize.getWidth() ||
+                     videoSz.getHeight() > maxPreviewSize.getHeight())) {
+                // Skip. Legacy mode can only do recording up to max preview size
+                continue;
+            }
+
             if (!mSupportedVideoSizes.contains(videoSz)) {
                 mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
                         profileId + " must be one of the camera device supported video size!");
                 continue;
             }
 
-            Size maxPreviewSize = mOrderedPreviewSizes.get(0);
-
             // For LEGACY, find closest supported smaller or equal JPEG size to the current video
             // size; if no size is smaller than the video, pick the smallest JPEG size.  The assert
             // for video size above guarantees that for LIMITED or FULL, we select videoSz here.
@@ -586,6 +653,8 @@
                     videoSnapshotSz = candidateSize;
                 }
             }
+            if (videoSnapshotSz.getWidth() * videoSnapshotSz.getHeight() > FRAME_SIZE_15M)
+                kFrameDrop_Tolerence = (int)(FRAMEDROP_TOLERANCE * FRAME_DROP_TOLERENCE_FACTOR);
 
             /**
              * Only test full res snapshot when below conditions are all true.
@@ -656,11 +725,17 @@
                 SystemClock.sleep(RECORDING_DURATION_MS / 2);
 
                 // Stop recording and preview
-                stopRecording(/* useMediaRecorder */true);
-                int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+                int durationMs = stopRecording(/* useMediaRecorder */true);
+                // For non-burst test, use number of frames to also double check video frame rate.
+                // Burst video snapshot is allowed to cause frame rate drop, so do not use number
+                // of frames to estimate duration
+                if (!burstTest) {
+                    durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+                        profile.videoFrameRate);
+                }
 
                 // Validation recorded video
-                validateRecording(videoSz, duration);
+                validateRecording(videoSz, durationMs);
 
                 if (burstTest) {
                     for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
@@ -693,8 +768,8 @@
                                 "Camera %d Video size %s: Number of dropped frames %d must not"
                                 + " be larger than %d",
                                 cameraId, videoSz.toString(), totalDroppedFrames,
-                                FRAMEDROP_TOLERANCE),
-                        FRAMEDROP_TOLERANCE, totalDroppedFrames);
+                                kFrameDrop_Tolerence),
+                        kFrameDrop_Tolerence, totalDroppedFrames);
             }
             closeImageReader();
         }
@@ -810,6 +885,7 @@
         } else {
             // TODO: need implement MediaCodec path.
         }
+        mRecordingStartTime = SystemClock.elapsedRealtime();
     }
 
     private void startRecording(boolean useMediaRecorder)  throws Exception {
@@ -826,7 +902,9 @@
         mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
     }
 
-    private void stopRecording(boolean useMediaRecorder) throws Exception {
+    // Stop recording and return the estimated video duration in milliseconds.
+    private int stopRecording(boolean useMediaRecorder) throws Exception {
+        long stopRecordingTime = SystemClock.elapsedRealtime();
         if (useMediaRecorder) {
             stopCameraStreaming();
 
@@ -840,6 +918,7 @@
             mRecordingSurface.release();
             mRecordingSurface = null;
         }
+        return (int) (stopRecordingTime - mRecordingStartTime);
     }
 
     private void releaseRecorder() {
@@ -853,14 +932,28 @@
         File outFile = new File(mOutMediaFileName);
         assertTrue("No video is recorded", outFile.exists());
 
-        MediaPlayer mediaPlayer = new MediaPlayer();
+        MediaExtractor extractor = new MediaExtractor();
         try {
-            mediaPlayer.setDataSource(mOutMediaFileName);
-            mediaPlayer.prepare();
-            Size videoSz = new Size(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
+            extractor.setDataSource(mOutMediaFileName);
+            long durationUs = 0;
+            int width = -1, height = -1;
+            int numTracks = extractor.getTrackCount();
+            final String VIDEO_MIME_TYPE = "video";
+            for (int i = 0; i < numTracks; i++) {
+                MediaFormat format = extractor.getTrackFormat(i);
+                String mime = format.getString(MediaFormat.KEY_MIME);
+                if (mime.contains(VIDEO_MIME_TYPE)) {
+                    Log.i(TAG, "video format is: " + format.toString());
+                    durationUs = format.getLong(MediaFormat.KEY_DURATION);
+                    width = format.getInteger(MediaFormat.KEY_WIDTH);
+                    height = format.getInteger(MediaFormat.KEY_HEIGHT);
+                    break;
+                }
+            }
+            Size videoSz = new Size(width, height);
             assertTrue("Video size doesn't match, expected " + sz.toString() +
                     " got " + videoSz.toString(), videoSz.equals(sz));
-            int duration = mediaPlayer.getDuration();
+            int duration = (int) (durationUs / 1000);
             if (VERBOSE) {
                 Log.v(TAG, String.format("Video duration: recorded %dms, expected %dms",
                                          duration, durationMs));
@@ -869,11 +962,12 @@
             // TODO: Don't skip this for video snapshot
             if (!mStaticInfo.isHardwareLevelLegacy()) {
                 assertTrue(String.format(
-                        "Video duration doesn't match: recorded %dms, expected %dms", duration,
-                        durationMs), Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
+                        "Camera %s: Video duration doesn't match: recorded %dms, expected %dms.",
+                        mCamera.getId(), duration, durationMs),
+                        Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
             }
         } finally {
-            mediaPlayer.release();
+            extractor.release();
             if (!DEBUG_DUMP) {
                 outFile.delete();
             }
@@ -937,7 +1031,7 @@
                         ));
                     }
 
-                    durationMs = (int) (nextTS - currentTS) / 1000000;
+                    durationMs = (nextTS - currentTS) / 1000000.0;
                     mCollector.expectTrue(
                             String.format(
                                     "Video %dx%d Frame drop detected after video snapshot: " +
@@ -951,7 +1045,7 @@
                     if (durationMs >= expectedDurationMs * 2) {
                         Log.w(TAG, String.format(
                                 "Video %dx%d Frame drop detected after video snapshot: " +
-                                        "duration %dms (expected %dms)",
+                                        "duration %fms (expected %fms)",
                                 mVideoSize.getWidth(), mVideoSize.getHeight(),
                                 durationMs, expectedDurationMs
                         ));
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index a6a7d10..abc0fea 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -19,16 +19,17 @@
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 import static android.hardware.camera2.cts.RobustnessTest.MaxOutputSizes.*;
 
+import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.params.StreamConfigurationMap;
-import android.hardware.camera2.cts.CameraTestUtils;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.media.CamcorderProfile;
@@ -36,12 +37,15 @@
 import android.media.ImageReader;
 import android.util.Log;
 import android.util.Size;
+import android.view.Display;
 import android.view.Surface;
+import android.view.WindowManager;
 
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.List;
 
+import static junit.framework.Assert.assertTrue;
 import static org.mockito.Mockito.*;
 
 /**
@@ -50,11 +54,14 @@
 public class RobustnessTest extends Camera2AndroidTestCase {
     private static final String TAG = "RobustnessTest";
 
-    private static final int FAILED_CONFIGURE_TIMEOUT = 5000; //ms
+    private static final int CONFIGURE_TIMEOUT = 5000; //ms
+    private static final int CAPTURE_TIMEOUT = 1000; //ms
 
     /**
-     * Test that a {@link CameraCaptureSession} configured with a {@link Surface} with invalid
-     * dimensions fails gracefully.
+     * Test that a {@link CameraCaptureSession} can be configured with a {@link Surface} containing
+     * a dimension other than one of the supported output dimensions.  The buffers produced into
+     * this surface are expected have the dimensions of the closest possible buffer size in the
+     * available stream configurations for a surface with this format.
      */
     public void testBadSurfaceDimensions() throws Exception {
         for (String id : mCameraIds) {
@@ -62,9 +69,25 @@
                 Log.i(TAG, "Testing Camera " + id);
                 openDevice(id);
 
-                // Setup Surface with unconfigured dimensions.
-                SurfaceTexture surfaceTexture = new SurfaceTexture(0);
-                Surface surface = new Surface(surfaceTexture);
+                // Find some size not supported by the camera
+                Size weirdSize = new Size(643, 577);
+                int count = 0;
+                while(mOrderedPreviewSizes.contains(weirdSize)) {
+                    // Really, they can't all be supported...
+                    weirdSize = new Size(weirdSize.getWidth() + 1, weirdSize.getHeight() + 1);
+                    count++;
+                    assertTrue("Too many exotic YUV_420_888 resolutions supported.", count < 100);
+                }
+
+                // Setup imageReader with invalid dimension
+                ImageReader imageReader = ImageReader.newInstance(weirdSize.getWidth(),
+                        weirdSize.getHeight(), ImageFormat.YUV_420_888, 3);
+
+                // Setup ImageReaderListener
+                SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+                imageReader.setOnImageAvailableListener(imageListener, mHandler);
+
+                Surface surface = imageReader.getSurface();
                 List<Surface> surfaces = new ArrayList<>();
                 surfaces.add(surface);
 
@@ -72,19 +95,38 @@
                 CaptureRequest.Builder request =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                 request.addTarget(surface);
-                CameraCaptureSession.CaptureCallback mockCaptureListener =
-                        mock(CameraCaptureSession.CaptureCallback.class);
 
                 // Check that correct session callback is hit.
                 CameraCaptureSession.StateCallback sessionListener =
                         mock(CameraCaptureSession.StateCallback.class);
-                mCamera.createCaptureSession(surfaces, sessionListener, mHandler);
-                verify(sessionListener, timeout(FAILED_CONFIGURE_TIMEOUT).atLeastOnce()).
-                        onConfigureFailed(any(CameraCaptureSession.class));
-                verify(sessionListener, never()).onConfigured(any(CameraCaptureSession.class));
+                CameraCaptureSession session = CameraTestUtils.configureCameraSession(mCamera,
+                        surfaces, sessionListener, mHandler);
+
+                verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+                        onConfigured(any(CameraCaptureSession.class));
+                verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+                        onReady(any(CameraCaptureSession.class));
+                verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class));
                 verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
-                verify(sessionListener, never()).onReady(any(CameraCaptureSession.class));
                 verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
+
+                CameraCaptureSession.CaptureCallback captureListener =
+                        mock(CameraCaptureSession.CaptureCallback.class);
+                session.capture(request.build(), captureListener, mHandler);
+
+                verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce()).
+                        onCaptureCompleted(any(CameraCaptureSession.class),
+                                any(CaptureRequest.class), any(TotalCaptureResult.class));
+                verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class),
+                        any(CaptureRequest.class), any(CaptureFailure.class));
+
+                Image image = imageListener.getImage(CAPTURE_TIMEOUT);
+                int imageWidth = image.getWidth();
+                int imageHeight = image.getHeight();
+                Size actualSize = new Size(imageWidth, imageHeight);
+
+                assertTrue("Camera does not contain outputted image resolution " + actualSize,
+                        mOrderedPreviewSizes.contains(actualSize));
             } finally {
                 closeDevice(id);
             }
@@ -126,10 +168,13 @@
             {YUV , PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM }  // Two-input in-app processing with still capture.
         };
 
-        final int[][] FULL_COMBINATIONS = {
+        final int[][] BURST_COMBINATIONS = {
             {PRIV, PREVIEW,  PRIV, MAXIMUM }, // Maximum-resolution GPU processing with preview.
             {PRIV, PREVIEW,  YUV,  MAXIMUM }, // Maximum-resolution in-app processing with preview.
             {YUV,  PREVIEW,  YUV,  MAXIMUM }, // Maximum-resolution two-input in-app processsing.
+        };
+
+        final int[][] FULL_COMBINATIONS = {
             {PRIV, PREVIEW,  PRIV, PREVIEW,  JPEG, MAXIMUM }, //Video recording with maximum-size video snapshot.
             {YUV,  VGA,      PRIV, PREVIEW,  YUV,  MAXIMUM }, // Standard video recording plus maximum-resolution in-app processing.
             {YUV,  VGA,      YUV,  PREVIEW,  YUV,  MAXIMUM } // Preview plus two-input maximum-resolution in-app processing.
@@ -147,7 +192,7 @@
         };
 
         final int[][][] TABLES =
-                { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
+            { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
 
         // Sanity check the tables
         int tableIdx = 0;
@@ -179,15 +224,10 @@
 
             CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
 
-            MaxOutputSizes maxSizes = new MaxOutputSizes(cc, id);
+            MaxOutputSizes maxSizes = new MaxOutputSizes(cc, id, getContext());
 
             final StaticMetadata staticInfo = new StaticMetadata(cc);
 
-            if (staticInfo.isHardwareLevelLegacy()) {
-                Log.i(TAG, "Skipping test on legacy devices");
-                continue;
-            }
-
             openDevice(id);
 
             // Always run legacy-level tests
@@ -206,7 +246,14 @@
                     testOutputCombination(id, config, maxSizes);
                 }
 
-                // Check for FULL and RAW and run those if appropriate
+                // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate
+
+                if (staticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
+                    for (int[] config : BURST_COMBINATIONS) {
+                        testOutputCombination(id, config, maxSizes);
+                    }
+                }
 
                 if (staticInfo.isHardwareLevelFull()) {
                     for (int[] config : FULL_COMBINATIONS) {
@@ -243,7 +290,7 @@
         static final int VGA = 3;
         static final int RESOLUTION_COUNT = 4;
 
-        public MaxOutputSizes(CameraCharacteristics cc, String cameraId) {
+        public MaxOutputSizes(CameraCharacteristics cc, String cameraId, Context context) {
             StreamConfigurationMap configs =
                     cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
             Size[] privSizes = configs.getOutputSizes(SurfaceTexture.class);
@@ -251,11 +298,13 @@
             Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
             Size[] rawSizes = configs.getOutputSizes(ImageFormat.RAW_SENSOR);
 
+            Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
+
             maxRawSize = (rawSizes != null) ? CameraTestUtils.getMaxSize(rawSizes) : null;
 
-            maxPrivSizes[PREVIEW] = getMaxSize(privSizes, PREVIEW_SIZE_BOUND);
-            maxYuvSizes[PREVIEW]  = getMaxSize(yuvSizes, PREVIEW_SIZE_BOUND);
-            maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, PREVIEW_SIZE_BOUND);
+            maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize);
+            maxYuvSizes[PREVIEW]  = getMaxSize(yuvSizes, maxPreviewSize);
+            maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize);
 
             maxPrivSizes[RECORD] = getMaxRecordingSize(cameraId);
             maxYuvSizes[RECORD]  = getMaxRecordingSize(cameraId);
@@ -322,26 +371,17 @@
         }
     }
 
-    private final class ImageCloser implements ImageReader.OnImageAvailableListener {
-        @Override
-        public void onImageAvailable(ImageReader reader) {
-            Image image = reader.acquireLatestImage();
-            if (image != null) {
-                image.close();
-            }
-        }
-    }
-
     private void testOutputCombination(String cameraId, int[] config, MaxOutputSizes maxSizes)
             throws Exception {
 
         Log.i(TAG, String.format("Testing Camera %s, config %s",
                         cameraId, MaxOutputSizes.configToString(config)));
 
-        final int TIMEOUT_FOR_RESULT_MS = 1000;
+        // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
+        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
         final int MIN_RESULT_COUNT = 3;
 
-        ImageCloser imageCloser = new ImageCloser();
+        ImageDropperListener imageDropperListener = new ImageDropperListener();
         // Set up outputs
         List<Object> outputTargets = new ArrayList<>();
         List<Surface> outputSurfaces = new ArrayList<>();
@@ -367,7 +407,7 @@
                     Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
                     ImageReader target = ImageReader.newInstance(
                         targetSize.getWidth(), targetSize.getHeight(), JPEG, MIN_RESULT_COUNT);
-                    target.setOnImageAvailableListener(imageCloser, mHandler);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
                     outputTargets.add(target);
                     outputSurfaces.add(target.getSurface());
                     jpegTargets.add(target);
@@ -377,7 +417,7 @@
                     Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
                     ImageReader target = ImageReader.newInstance(
                         targetSize.getWidth(), targetSize.getHeight(), YUV, MIN_RESULT_COUNT);
-                    target.setOnImageAvailableListener(imageCloser, mHandler);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
                     outputTargets.add(target);
                     outputSurfaces.add(target.getSurface());
                     yuvTargets.add(target);
@@ -387,7 +427,7 @@
                     Size targetSize = maxSizes.maxRawSize;
                     ImageReader target = ImageReader.newInstance(
                         targetSize.getWidth(), targetSize.getHeight(), RAW, MIN_RESULT_COUNT);
-                    target.setOnImageAvailableListener(imageCloser, mHandler);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
                     outputTargets.add(target);
                     outputSurfaces.add(target.getSurface());
                     rawTargets.add(target);
@@ -514,4 +554,35 @@
         return sz;
     }
 
+    private static Size getMaxPreviewSize(Context context, String cameraId) {
+        try {
+            WindowManager windowManager =
+                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+            Display display = windowManager.getDefaultDisplay();
+
+            int width = display.getWidth();
+            int height = display.getHeight();
+
+            if (height > width) {
+                height = width;
+                width = display.getHeight();
+            }
+
+            CameraManager camMgr =
+                (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+            List<Size> orderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
+                cameraId, camMgr, PREVIEW_SIZE_BOUND);
+
+            if (orderedPreviewSizes != null) {
+                for (Size size : orderedPreviewSizes) {
+                    if (width >= size.getWidth() &&
+                        height >= size.getHeight())
+                        return size;
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "getMaxPreviewSize Failed. "+e.toString());
+        }
+        return PREVIEW_SIZE_BOUND;
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
index dd1882e..b8c2f2e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -21,6 +21,7 @@
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.CameraCharacteristics.Key;
@@ -29,11 +30,11 @@
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Size;
 
-import junit.framework.Assert;
-
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -55,6 +56,9 @@
     private static final float MIN_FPS_FOR_FULL_DEVICE = 20.0f;
     private String mCameraId;
 
+    // Last defined capability enum, for iterating over all of them
+    private static final int LAST_CAPABILITY_ENUM = REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE;
+
     /**
      * Test the available capability for different hardware support level devices.
      */
@@ -74,18 +78,13 @@
 
             if (mStaticInfo.isHardwareLevelFull()) {
                 // Capability advertisement must be right.
-                mCollector.expectTrue("Full device must contains MANUAL_SENSOR capability",
+                mCollector.expectTrue("Full device must contain MANUAL_SENSOR capability",
                         availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR));
-                mCollector.expectTrue("Full device must contains MANUAL_POST_PROCESSING capability",
+                mCollector.expectTrue("Full device must contain MANUAL_POST_PROCESSING capability",
                         availableCaps.contains(
                                 REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING));
-
-                // Max yuv resolution must be very close to  sensor resolution
-                Size[] yuvSizes = configs.getOutputSizes(ImageFormat.YUV_420_888);
-                Size maxYuvSize = CameraTestUtils.getMaxSize(yuvSizes);
-                mCollector.expectSizesAreSimilar(
-                        "Active array size and max YUV size should be similar",
-                        sensorSize, maxYuvSize, SIZE_ERROR_MARGIN);
+                mCollector.expectTrue("Full device must contain BURST_CAPTURE capability",
+                        availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE));
 
                 // Max resolution fps must be >= 20.
                 mCollector.expectTrue("Full device must support at least 20fps for max resolution",
@@ -96,6 +95,12 @@
                         mStaticInfo.isPerFrameControlSupported());
             }
 
+            if (availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                mCollector.expectTrue("MANUAL_SENSOR capability always requires " +
+                        "READ_SENSOR_SETTINGS capability as well",
+                        availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS));
+            }
+
             // Max jpeg resolution must be very close to  sensor resolution
             Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
             Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
@@ -144,7 +149,7 @@
             List<Integer> availableCaps = mStaticInfo.getAvailableCapabilitiesChecked();
 
             for (Integer capability = REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
-                    capability <= REQUEST_AVAILABLE_CAPABILITIES_RAW; capability++) {
+                    capability <= LAST_CAPABILITY_ENUM; capability++) {
                 boolean isCapabilityAvailable = availableCaps.contains(capability);
                 validateCapability(capability, isCapabilityAvailable);
             }
@@ -250,6 +255,10 @@
 
     private void validateCapability(Integer capability, boolean isCapabilityAvailable) {
         List<CaptureRequest.Key<?>> requestKeys = new ArrayList<>();
+        Set<CaptureResult.Key<?>> resultKeys = new HashSet<>();
+        // Capability requirements other than key presences
+        List<Pair<String, Boolean>> additionalRequirements = new ArrayList<>();
+
         /* For available capabilities, only check request keys in this test
            Characteristics keys are tested in ExtendedCameraCharacteristicsTest
            Result keys are tested in CaptureResultTest */
@@ -308,11 +317,31 @@
                 requestKeys.add(CaptureRequest.COLOR_CORRECTION_TRANSFORM);
                 requestKeys.add(CaptureRequest.SHADING_MODE);
                 requestKeys.add(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE);
-                if (mStaticInfo.isHardwareLevelFull()) {
-                    requestKeys.add(CaptureRequest.TONEMAP_CURVE);
-                    requestKeys.add(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE);
-                }
+                requestKeys.add(CaptureRequest.TONEMAP_CURVE);
+                requestKeys.add(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE);
 
+                // Legacy mode always doesn't support these requirements
+                Boolean contrastCurveModeSupported = false;
+                Boolean offColorAberrationModeSupported = false;
+                if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+                    int[] tonemapModes = mStaticInfo.getAvailableToneMapModesChecked();
+                    List<Integer> modeList = (tonemapModes.length == 0) ?
+                            new ArrayList<Integer>() :
+                            Arrays.asList(CameraTestUtils.toObject(tonemapModes));
+                    contrastCurveModeSupported =
+                            modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE);
+                    int[] colorAberrationModes =
+                            mStaticInfo.getAvailableColorAberrationModesChecked();
+                    modeList = (colorAberrationModes.length == 0) ?
+                            new ArrayList<Integer>() :
+                            Arrays.asList(CameraTestUtils.toObject(colorAberrationModes));
+                    offColorAberrationModeSupported =
+                            modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF);
+                }
+                additionalRequirements.add(new Pair<String, Boolean>(
+                        "Tonemap mode must include CONTRAST_CURVE", contrastCurveModeSupported));
+                additionalRequirements.add(new Pair<String, Boolean>(
+                        "Color aberration mode must include OFF", offColorAberrationModeSupported));
                 break;
             case REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR:
                 capabilityName = "REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR";
@@ -321,6 +350,7 @@
                 requestKeys.add(CaptureRequest.SENSOR_SENSITIVITY);
                 if (mStaticInfo.hasFocuser()) {
                     requestKeys.add(CaptureRequest.LENS_APERTURE);
+                    requestKeys.add(CaptureRequest.LENS_FOCUS_DISTANCE);
                     requestKeys.add(CaptureRequest.LENS_FILTER_DENSITY);
                     requestKeys.add(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE);
                 }
@@ -330,18 +360,68 @@
                 // RAW_CAPABILITY needs to check for not just capture request keys
                 validateRawCapability(isCapabilityAvailable);
                 return;
+            case REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE:
+                // Tested in ExtendedCameraCharacteristicsTest
+                return;
+            case REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS:
+                capabilityName = "REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS";
+                resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
+                resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
+                resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
+                if (mStaticInfo.hasFocuser()) {
+                    resultKeys.add(CaptureResult.LENS_APERTURE);
+                    resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+                    resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
+                }
+                break;
             default:
                 capabilityName = "Unknown";
-                Assert.fail(String.format("Unknown capability: %d", capability));
+                assertTrue(String.format("Unknown capability set: %d", capability),
+                           !isCapabilityAvailable);
+                return;
         }
 
-        boolean matchExpectation =
-                validateRequestKeysPresence(capabilityName, requestKeys, isCapabilityAvailable);
+        // Check additional requirements and exit early if possible
+        if (!isCapabilityAvailable) {
+            for (Pair<String, Boolean> p : additionalRequirements) {
+                String requirement = p.first;
+                Boolean meetRequirement = p.second;
+                // No further check is needed if we've found why capability cannot be advertised
+                if (!meetRequirement) {
+                    Log.v(TAG, String.format(
+                            "Camera %s doesn't list capability %s because of requirement: %s",
+                            mCameraId, capabilityName, requirement));
+                    return;
+                }
+            }
+        }
+
+        boolean matchExpectation = true;
+        if (!requestKeys.isEmpty()) {
+            matchExpectation &= validateRequestKeysPresence(
+                    capabilityName, requestKeys, isCapabilityAvailable);
+        }
+        if(!resultKeys.isEmpty()) {
+            matchExpectation &= validateResultKeysPresence(
+                    capabilityName, resultKeys, isCapabilityAvailable);
+        }
+
+        // Check additional requirements
+        for (Pair<String, Boolean> p : additionalRequirements) {
+            String requirement = p.first;
+            Boolean meetRequirement = p.second;
+            if (isCapabilityAvailable && !meetRequirement) {
+                mCollector.addMessage(String.format(
+                        "Camera %s list capability %s but does not meet the requirement: %s",
+                        mCameraId, capabilityName, requirement));
+            }
+        }
+
         // In case of isCapabilityAvailable == true, error has been filed in
-        // validateRequestKeysPresence
+        // validateRequest/ResultKeysPresence
         if (!matchExpectation && !isCapabilityAvailable) {
             mCollector.addMessage(String.format(
-                    "Camera %s doesn't list capability %s but contain all required keys",
+                    "Camera %s doesn't list capability %s but meets all requirements",
                     mCameraId, capabilityName));
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
index 37eff10..b4113e5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -71,6 +71,7 @@
     private static final Location sTestLocation0 = new Location(LocationManager.GPS_PROVIDER);
     private static final Location sTestLocation1 = new Location(LocationManager.GPS_PROVIDER);
     private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER);
+    private static final int RELAXED_CAPTURE_IMAGE_TIMEOUT_MS = CAPTURE_IMAGE_TIMEOUT_MS + 1000;
     static {
         sTestLocation0.setTime(1199145600L);
         sTestLocation0.setLatitude(37.736071);
@@ -263,11 +264,6 @@
                 Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
                 openDevice(id);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test for legacy devices");
-                    continue;
-                }
-
                 previewStillCombinationTestByCamera();
             } finally {
                 closeDevice();
@@ -314,11 +310,6 @@
                 Log.i(TAG, "Testing AE regions for Camera " + id);
                 openDevice(id);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
-
                 boolean aeRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AE_INDEX);
                 if (!aeRegionsSupported) {
                     continue;
@@ -369,11 +360,6 @@
                 Log.i(TAG, "Testing AF regions for Camera " + id);
                 openDevice(id);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
-
                 boolean afRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AF_INDEX);
                 if (!afRegionsSupported) {
                     continue;
@@ -659,7 +645,8 @@
                 prepareStillCaptureAndStartPreview(previewRequest, stillRequest, previewSz,
                         stillSz, resultListener, imageListener);
                 mSession.capture(stillRequest.build(), resultListener, mHandler);
-                Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                Image image = imageListener.getImage((mStaticInfo.isHardwareLevelLegacy()) ?
+                        RELAXED_CAPTURE_IMAGE_TIMEOUT_MS : CAPTURE_IMAGE_TIMEOUT_MS);
                 validateJpegCapture(image, stillSz);
 
                 // Free image resources
@@ -1262,6 +1249,11 @@
 
     private void aeCompensationTestByCamera() throws Exception {
         Range<Integer> compensationRange = mStaticInfo.getAeCompensationRangeChecked();
+        // Skip the test if exposure compensation is not supported.
+        if (compensationRange.equals(Range.create(0, 0))) {
+            return;
+        }
+
         Rational step = mStaticInfo.getAeCompensationStepChecked();
         float stepF = (float) step.getNumerator() / step.getDenominator();
         int stepsPerEv = (int) Math.round(1.0 / stepF);
@@ -1276,6 +1268,8 @@
         CaptureRequest.Builder stillRequest =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
         stillRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
+        CaptureResult normalResult;
+        CaptureResult compensatedResult;
 
         // The following variables should only be read under the MANUAL_SENSOR capability guard:
         long minExposureValue = -1;
@@ -1307,13 +1301,13 @@
 
             // Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
             waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
-            CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            normalResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
 
             long normalExposureValue = -1;
             if (mStaticInfo.isCapabilitySupported(
                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                 // get and check if current exposure value is valid
-                normalExposureValue = getExposureValue(result);
+                normalExposureValue = getExposureValue(normalResult);
                 mCollector.expectInRange("Exposure setting out of bound", normalExposureValue,
                         minExposureValue, maxExposureValuePreview);
 
@@ -1323,6 +1317,12 @@
                     expectedExposureValue > maxExposureValueStill) {
                     continue;
                 }
+                Log.v(TAG, "Expect ratio: " + expectedRatio +
+                        " normalExposureValue: " + normalExposureValue +
+                        " expectedExposureValue: " + expectedExposureValue +
+                        " minExposureValue: " + minExposureValue +
+                        " maxExposureValuePreview: " + maxExposureValuePreview +
+                        " maxExposureValueStill: " + maxExposureValueStill);
             }
 
             // Now issue exposure compensation and wait for AE locked. AE could take a few
@@ -1343,29 +1343,39 @@
             CaptureRequest request = stillRequest.build();
             mSession.capture(request, resultListener, mHandler);
 
-            result = resultListener.getCaptureResultForRequest(request, WAIT_FOR_RESULT_TIMEOUT_MS);
+            compensatedResult = resultListener.getCaptureResultForRequest(
+                    request, WAIT_FOR_RESULT_TIMEOUT_MS);
 
             if (mStaticInfo.isCapabilitySupported(
                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                 // Verify the exposure value compensates as requested
-                long compensatedExposureValue = getExposureValue(result);
+                long compensatedExposureValue = getExposureValue(compensatedResult);
                 mCollector.expectInRange("Exposure setting out of bound", compensatedExposureValue,
                         minExposureValue, maxExposureValueStill);
                 double observedRatio = (double) compensatedExposureValue / normalExposureValue;
                 double error = observedRatio / expectedRatio;
-                mCollector.expectInRange(String.format(
-                        "Exposure compensation ratio exceeds error tolerence:"
-                        + " expected(%f) observed(%f) ", expectedRatio, observedRatio),
-                        error,
+                String errorString = String.format(
+                        "Exposure compensation ratio exceeds error tolerence:" +
+                        " expected(%f) observed(%f)." +
+                        " Normal exposure time %d us, sensitivity %d." +
+                        " Compensated exposure time %d us, sensitivity %d",
+                        expectedRatio, observedRatio,
+                        (int) (getValueNotNull(
+                                normalResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
+                        getValueNotNull(normalResult, CaptureResult.SENSOR_SENSITIVITY),
+                        (int) (getValueNotNull(
+                                compensatedResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
+                        getValueNotNull(compensatedResult, CaptureResult.SENSOR_SENSITIVITY));
+                mCollector.expectInRange(errorString, error,
                         1.0 - AE_COMPENSATION_ERROR_TOLERANCE,
                         1.0 + AE_COMPENSATION_ERROR_TOLERANCE);
             }
 
             mCollector.expectEquals("Exposure compensation result should match requested value.",
                     exposureCompensation,
-                    result.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
+                    compensatedResult.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
             mCollector.expectTrue("Exposure lock should be set",
-                    result.get(CaptureResult.CONTROL_AE_LOCK));
+                    compensatedResult.get(CaptureResult.CONTROL_AE_LOCK));
 
             Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
             validateJpegCapture(image, maxStillSz);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index b3d4cf9..01da4c8 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -74,11 +74,6 @@
                 Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
                 openDevice(mCameraIds[i]);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
-
                 previewTestByCamera();
             } finally {
                 closeDevice();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
index f4859e5..0ee5ffc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
@@ -859,8 +859,25 @@
      */
     public <T> void expectKeyValueIsIn(CameraCharacteristics characteristics,
                                        CameraCharacteristics.Key<T> key, T... expected) {
-        T value;
-        if ((value = expectKeyValueNotNull(characteristics, key)) == null) {
+        T value = expectKeyValueNotNull(characteristics, key);
+        if (value == null) {
+            return;
+        }
+        String reason = "Key " + key.getName() + " value " + value
+                + " isn't one of the expected values " + Arrays.deepToString(expected);
+        expectContains(reason, expected, value);
+    }
+
+    /**
+     * Check if the key is non-null, and the key value is one of the expected values.
+     *
+     * @param request The The {@link CaptureRequest#Builder} to get the key from.
+     * @param key The {@link CaptureRequest} key to be checked.
+     * @param expected The expected values of the CaptureRequest key.
+     */
+    public <T> void expectKeyValueIsIn(Builder request, CaptureRequest.Key<T> key, T... expected) {
+        T value = expectKeyValueNotNull(request, key);
+        if (value == null) {
             return;
         }
         String reason = "Key " + key.getName() + " value " + value
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index a5c7083..2a654db 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -436,17 +436,23 @@
         int[] modes = getValueFromKeyNonNull(key);
 
         boolean foundAuto = false;
+        boolean found50Hz = false;
+        boolean found60Hz = false;
         for (int mode : modes) {
             checkTrueForKey(key, "mode value " + mode + " is out if range",
                     mode >= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF ||
                     mode <= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
             if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO) {
                 foundAuto = true;
-                return modes;
+            } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ) {
+                found50Hz = true;
+            } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ) {
+                found60Hz = true;
             }
         }
-        // Must contain AUTO mode.
-        checkTrueForKey(key, "AUTO mode is missing", foundAuto);
+        // Must contain AUTO mode or one of 50/60Hz mode.
+        checkTrueForKey(key, "Either AUTO mode or both 50HZ/60HZ mode should present",
+                foundAuto || (found50Hz && found60Hz));
 
         return modes;
     }
@@ -660,8 +666,9 @@
         List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
         checkTrueForKey(key, " Camera devices must always support FAST mode",
                 modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
-        if (isHardwareLevelFull()) {
-            checkTrueForKey(key, "Full-capability camera devices must support"
+        if (isCapabilitySupported(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING)) {
+            checkTrueForKey(key, "MANUAL_POST_PROCESSING supported camera devices must support"
                     + "CONTRAST_CURVE mode",
                     modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE) &&
                     modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
@@ -1302,12 +1309,13 @@
         final Range<Integer> DEFAULT_RANGE = Range.create(
                 (int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MIN / compensationStepF),
                 (int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MAX / compensationStepF));
+        final Range<Integer> ZERO_RANGE = Range.create(0, 0);
         if (compensationRange == null) {
-            return DEFAULT_RANGE;
+            return ZERO_RANGE;
         }
 
         // Legacy devices don't have a minimum range requirement
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelLimitedOrBetter() && !compensationRange.equals(ZERO_RANGE)) {
             checkTrueForKey(key, " range value must be at least " + DEFAULT_RANGE
                     + ", actual " + compensationRange + ", compensation step " + compensationStep,
                    compensationRange.getLower() <= DEFAULT_RANGE.getLower() &&
@@ -1433,8 +1441,9 @@
         }
 
         List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
-        checkTrueForKey(key, " Camera devices must always support OFF mode",
-                modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF));
+        checkTrueForKey(key, " Camera devices must always support either OFF or FAST mode",
+                modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF) ||
+                modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_FAST));
         checkElementDistinct(key, modeList);
         checkArrayValuesInRange(key, modes,
                 CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF,
@@ -1480,7 +1489,7 @@
 
         checkArrayValuesInRange(key, availableCaps,
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW);
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
         capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
         return capList;
     }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
new file mode 100644
index 0000000..744d2c7
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
@@ -0,0 +1,93 @@
+/*
+ * 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 android.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicHistogram;
+
+/**
+ * Utility class providing methods for various pixel-wise ARGB bitmap operations.
+ */
+public class BitmapUtils {
+    private static final String TAG = "BitmapUtils";
+    private static final int COLOR_BIT_DEPTH = 256;
+
+    public static int A = 3;
+    public static int R = 0;
+    public static int G = 1;
+    public static int B = 2;
+    public static int NUM_CHANNELS = 4;
+
+    /**
+     * Return the histograms for each color channel (interleaved).
+     *
+     * @param rs a {@link RenderScript} context to use.
+     * @param bmap a {@link Bitmap} to generate the histograms for.
+     * @return an array containing NUM_CHANNELS * COLOR_BIT_DEPTH histogram bucket values, with
+     * the color channels interleaved.
+     */
+    public static int[] calcHistograms(RenderScript rs, Bitmap bmap) {
+        ScriptIntrinsicHistogram hist = ScriptIntrinsicHistogram.create(rs, Element.U8_4(rs));
+        Allocation sums = Allocation.createSized(rs, Element.I32_4(rs), COLOR_BIT_DEPTH);
+
+        // Setup input allocation (ARGB 8888 bitmap).
+        Allocation input = Allocation.createFromBitmap(rs, bmap);
+
+        hist.setOutput(sums);
+        hist.forEach(input);
+        int[] output = new int[COLOR_BIT_DEPTH * NUM_CHANNELS];
+        sums.copyTo(output);
+        return output;
+    }
+
+    /**
+     * Find the difference between two bitmaps using average of per-pixel differences.
+     *
+     * @param a first {@link android.graphics.Bitmap}.
+     * @param b second {@link android.graphics.Bitmap}.
+     * @return the difference.
+     */
+    public static double calcDifferenceMetric(Bitmap a, Bitmap b) {
+        if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
+            throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
+                    a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
+                    b.getHeight());
+        }
+        // TODO: Optimize this in renderscript to avoid copy.
+        int[] aPixels = new int[a.getHeight() * a.getWidth()];
+        int[] bPixels = new int[aPixels.length];
+        a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
+                a.getHeight());
+        b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
+                b.getHeight());
+        double diff = 0;
+        for (int i = 0; i < aPixels.length; i++) {
+            int aPix = aPixels[i];
+            int bPix = bPixels[i];
+
+            diff += Math.abs(Color.red(aPix) - Color.red(bPix)); // red
+            diff += Math.abs(Color.green(aPix) - Color.green(bPix)); // green
+            diff += Math.abs(Color.blue(aPix) - Color.blue(bPix)); // blue
+        }
+        diff /= (aPixels.length * 3);
+        return diff;
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
new file mode 100644
index 0000000..33c212a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
@@ -0,0 +1,796 @@
+/*
+ * 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 android.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.LensShadingMap;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+import android.renderscript.Int4;
+import android.renderscript.Matrix3f;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+
+import android.hardware.camera2.cts.ScriptC_raw_converter;
+import android.util.Log;
+import android.util.Rational;
+import android.util.SparseIntArray;
+
+import java.util.Arrays;
+
+/**
+ * Utility class providing methods for rendering RAW16 images into other colorspaces.
+ */
+public class RawConverter {
+    private static final String TAG = "RawConverter";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    /**
+     * Matrix to convert from CIE XYZ colorspace to sRGB, Bradford-adapted to D65.
+     */
+    private static final float[] sXYZtoRGBBradford = new float[] {
+            3.1338561f, -1.6168667f, -0.4906146f,
+            -0.9787684f, 1.9161415f, 0.0334540f,
+            0.0719453f, -0.2289914f, 1.4052427f
+    };
+
+    /**
+     * Matrix to convert from the ProPhoto RGB colorspace to CIE XYZ colorspace.
+     */
+    private static final float[] sProPhotoToXYZ = new float[] {
+            0.797779f, 0.135213f, 0.031303f,
+            0.288000f, 0.711900f, 0.000100f,
+            0.000000f, 0.000000f, 0.825105f
+    };
+
+    /**
+     * Matrix to convert from CIE XYZ colorspace to ProPhoto RGB colorspace.
+     */
+    private static final float[] sXYZtoProPhoto = new float[] {
+            1.345753f, -0.255603f, -0.051025f,
+            -0.544426f, 1.508096f, 0.020472f,
+            0.000000f, 0.000000f, 1.211968f
+    };
+
+    /**
+     * Coefficients for a 3rd order polynomial, ordered from highest to lowest power.  This
+     * polynomial approximates the default tonemapping curve used for ACR3.
+     */
+    private static final float[] DEFAULT_ACR3_TONEMAP_CURVE_COEFFS = new float[] {
+            -1.087f,  1.643f,  0.443f, 0f
+    };
+
+    /**
+     * The D50 whitepoint coordinates in CIE XYZ colorspace.
+     */
+    private static final float[] D50_XYZ = new float[] { 0.9642f, 1, 0.8249f };
+
+    /**
+     * An array containing the color temperatures for standard reference illuminants.
+     */
+    private static final SparseIntArray sStandardIlluminants = new SparseIntArray();
+    private static final int NO_ILLUMINANT = -1;
+    static {
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT, 6504);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D65, 6504);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D50, 5003);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D55, 5503);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D75, 7504);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_A, 2856);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_B, 4874);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_C, 6774);
+        sStandardIlluminants.append(
+                CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT_FLUORESCENT, 6430);
+        sStandardIlluminants.append(
+                CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT, 4230);
+        sStandardIlluminants.append(
+                CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_WHITE_FLUORESCENT, 3450);
+        // TODO: Add the rest of the illuminants included in the LightSource EXIF tag.
+    }
+
+    /**
+     * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+     *
+     * <p> This function applies the operations roughly outlined in the Adobe DNG specification
+     * using the provided metadata about the image sensor.  Sensor data for Android devices is
+     * assumed to be relatively linear, and no extra linearization step is applied here.  The
+     * following operations are applied in the given order:</p>
+     *
+     * <ul>
+     *     <li>
+     *         Black level subtraction - the black levels given in the SENSOR_BLACK_LEVEL_PATTERN
+     *         tag are subtracted from the corresponding raw pixels.
+     *     </li>
+     *     <li>
+     *         Rescaling - each raw pixel is scaled by 1/(white level - black level).
+     *     </li>
+     *     <li>
+     *         Lens shading correction - the interpolated gains from the gain map defined in the
+     *         STATISTICS_LENS_SHADING_CORRECTION_MAP are applied to each raw pixel.
+     *     </li>
+     *     <li>
+     *         Clipping - each raw pixel is clipped to a range of [0.0, 1.0].
+     *     </li>
+     *     <li>
+     *         Demosaic - the RGB channels for each pixel are retrieved from the Bayer mosaic
+     *         of raw pixels using a simple bilinear-interpolation demosaicing algorithm.
+     *     </li>
+     *     <li>
+     *         Colorspace transform to wide-gamut RGB - each pixel is mapped into a
+     *         wide-gamut colorspace (in this case ProPhoto RGB is used) from the sensor
+     *         colorspace.
+     *     </li>
+     *     <li>
+     *         Tonemapping - A basic tonemapping curve using the default from ACR3 is applied
+     *         (no further exposure compensation is applied here, though this could be improved).
+     *     </li>
+     *     <li>
+     *         Colorspace transform to final RGB - each pixel is mapped into linear sRGB colorspace.
+     *     </li>
+     *     <li>
+     *         Gamma correction - each pixel is gamma corrected using γ=2.2 to map into sRGB
+     *         colorspace for viewing.
+     *     </li>
+     *     <li>
+     *         Packing - each pixel is scaled so that each color channel has a range of [0, 255],
+     *         and is packed into an Android bitmap.
+     *     </li>
+     * </ul>
+     *
+     * <p> Arguments given here are assumed to come from the values for the corresponding
+     * {@link CameraCharacteristics.Key}s defined for the camera that produced this RAW16 buffer.
+     * </p>
+     * @param rs a {@link RenderScript} context to use.
+     * @param inputWidth width of the input RAW16 image in pixels.
+     * @param inputHeight height of the input RAW16 image in pixels.
+     * @param inputStride stride of the input RAW16 image in bytes.
+     * @param rawImageInput a byte array containing a RAW16 image.
+     * @param staticMetadata the {@link CameraCharacteristics} for this RAW capture.
+     * @param dynamicMetadata the {@link CaptureResult} for this RAW capture.
+     * @param outputOffsetX the offset width into the raw image of the left side of the output
+     *                      rectangle.
+     * @param outputOffsetY the offset height into the raw image of the top side of the output
+     *                      rectangle.
+     * @param argbOutput a {@link Bitmap} to output the rendered RAW image into.  The height and
+     *                   width of this bitmap along with the output offsets are used to determine
+     *                   the dimensions and offset of the output rectangle contained in the RAW
+     *                   image to be rendered.
+     */
+    public static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight,
+            int inputStride, byte[] rawImageInput, CameraCharacteristics staticMetadata,
+            CaptureResult dynamicMetadata, int outputOffsetX, int outputOffsetY,
+            /*out*/Bitmap argbOutput) {
+        int cfa = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+        int[] blackLevelPattern = new int[4];
+        staticMetadata.get(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN).
+                copyTo(blackLevelPattern, /*offset*/0);
+        int whiteLevel = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
+        int ref1 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1);
+        int ref2 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2);
+        float[] calib1 = new float[9];
+        float[] calib2 = new float[9];
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1), calib1);
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2), calib2);
+        float[] color1 = new float[9];
+        float[] color2 = new float[9];
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1), color1);
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2), color2);
+        float[] forward1 = new float[9];
+        float[] forward2 = new float[9];
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1), forward1);
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2), forward2);
+
+        Rational[] neutral = dynamicMetadata.get(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
+
+        LensShadingMap shadingMap = dynamicMetadata.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+
+        convertToSRGB(rs, inputWidth, inputHeight, inputStride, cfa, blackLevelPattern, whiteLevel,
+                rawImageInput, ref1, ref2, calib1, calib2, color1, color2,
+                forward1, forward2, neutral, shadingMap, outputOffsetX, outputOffsetY, argbOutput);
+    }
+
+    /**
+     * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+     *
+     * @see #convertToSRGB
+     */
+    private static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight,
+            int inputStride, int cfa, int[] blackLevelPattern, int whiteLevel, byte[] rawImageInput,
+            int referenceIlluminant1, int referenceIlluminant2, float[] calibrationTransform1,
+            float[] calibrationTransform2, float[] colorMatrix1, float[] colorMatrix2,
+            float[] forwardTransform1, float[] forwardTransform2, Rational[/*3*/] neutralColorPoint,
+            LensShadingMap lensShadingMap, int outputOffsetX, int outputOffsetY,
+            /*out*/Bitmap argbOutput) {
+
+        // Validate arguments
+        if (argbOutput == null || rs == null || rawImageInput == null) {
+            throw new IllegalArgumentException("Null argument to convertToSRGB");
+        }
+        if (argbOutput.getConfig() != Bitmap.Config.ARGB_8888) {
+            throw new IllegalArgumentException(
+                    "Output bitmap passed to convertToSRGB is not ARGB_8888 format");
+        }
+        if (outputOffsetX < 0 || outputOffsetY < 0) {
+            throw new IllegalArgumentException("Negative offset passed to convertToSRGB");
+        }
+        if ((inputStride / 2) < inputWidth) {
+            throw new IllegalArgumentException("Stride too small.");
+        }
+        if ((inputStride % 2) != 0) {
+            throw new IllegalArgumentException("Invalid stride for RAW16 format, see graphics.h.");
+        }
+        int outWidth = argbOutput.getWidth();
+        int outHeight = argbOutput.getHeight();
+        if (outWidth + outputOffsetX > inputWidth || outHeight + outputOffsetY > inputHeight) {
+            throw new IllegalArgumentException("Raw image with dimensions (w=" + inputWidth +
+                    ", h=" + inputHeight + "), cannot converted into sRGB image with dimensions (w="
+                    + outWidth + ", h=" + outHeight + ").");
+        }
+        if (cfa < 0 || cfa > 3) {
+            throw new IllegalArgumentException("Unsupported cfa pattern " + cfa + " used.");
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Metadata Used:");
+            Log.d(TAG, "Input width,height: " + inputWidth + "," + inputHeight);
+            Log.d(TAG, "Output offset x,y: " + outputOffsetX + "," + outputOffsetY);
+            Log.d(TAG, "Output width,height: " + outWidth + "," + outHeight);
+            Log.d(TAG, "CFA: " + cfa);
+            Log.d(TAG, "BlackLevelPattern: " + Arrays.toString(blackLevelPattern));
+            Log.d(TAG, "WhiteLevel: " + whiteLevel);
+            Log.d(TAG, "ReferenceIlluminant1: " + referenceIlluminant1);
+            Log.d(TAG, "ReferenceIlluminant2: " + referenceIlluminant2);
+            Log.d(TAG, "CalibrationTransform1: " + Arrays.toString(calibrationTransform1));
+            Log.d(TAG, "CalibrationTransform2: " + Arrays.toString(calibrationTransform2));
+            Log.d(TAG, "ColorMatrix1: " + Arrays.toString(colorMatrix1));
+            Log.d(TAG, "ColorMatrix2: " + Arrays.toString(colorMatrix2));
+            Log.d(TAG, "ForwardTransform1: " + Arrays.toString(forwardTransform1));
+            Log.d(TAG, "ForwardTransform2: " + Arrays.toString(forwardTransform2));
+            Log.d(TAG, "NeutralColorPoint: " + Arrays.toString(neutralColorPoint));
+        }
+
+        Allocation gainMap = null;
+        if (lensShadingMap != null) {
+            float[] lsm = new float[lensShadingMap.getGainFactorCount()];
+            lensShadingMap.copyGainFactors(/*inout*/lsm, /*offset*/0);
+            gainMap = createFloat4Allocation(rs, lsm, lensShadingMap.getColumnCount(),
+                    lensShadingMap.getRowCount());
+        }
+
+        float[] normalizedForwardTransform1 = Arrays.copyOf(forwardTransform1,
+                forwardTransform1.length);
+        normalizeFM(normalizedForwardTransform1);
+        float[] normalizedForwardTransform2 = Arrays.copyOf(forwardTransform2,
+                forwardTransform2.length);
+        normalizeFM(normalizedForwardTransform2);
+
+        float[] normalizedColorMatrix1 = Arrays.copyOf(colorMatrix1, colorMatrix1.length);
+        normalizeCM(normalizedColorMatrix1);
+        float[] normalizedColorMatrix2 = Arrays.copyOf(colorMatrix2, colorMatrix2.length);
+        normalizeCM(normalizedColorMatrix2);
+
+        if (DEBUG) {
+            Log.d(TAG, "Normalized ForwardTransform1: " + Arrays.toString(normalizedForwardTransform1));
+            Log.d(TAG, "Normalized ForwardTransform2: " + Arrays.toString(normalizedForwardTransform2));
+            Log.d(TAG, "Normalized ColorMatrix1: " + Arrays.toString(normalizedColorMatrix1));
+            Log.d(TAG, "Normalized ColorMatrix2: " + Arrays.toString(normalizedColorMatrix2));
+        }
+
+        // Calculate full sensor colorspace to sRGB colorspace transform.
+        double interpolationFactor = findDngInterpolationFactor(referenceIlluminant1,
+                referenceIlluminant2, calibrationTransform1, calibrationTransform2,
+                normalizedColorMatrix1, normalizedColorMatrix2, neutralColorPoint);
+        if (DEBUG) Log.d(TAG, "Interpolation factor used: " + interpolationFactor);
+        float[] sensorToXYZ = new float[9];
+        calculateCameraToXYZD50Transform(normalizedForwardTransform1, normalizedForwardTransform2,
+                calibrationTransform1, calibrationTransform2, neutralColorPoint,
+                interpolationFactor, /*out*/sensorToXYZ);
+        if (DEBUG) Log.d(TAG, "CameraToXYZ xform used: " + Arrays.toString(sensorToXYZ));
+        float[] sensorToProPhoto = new float[9];
+        multiply(sXYZtoProPhoto, sensorToXYZ, /*out*/sensorToProPhoto);
+        if (DEBUG) Log.d(TAG, "CameraToIntemediate xform used: " + Arrays.toString(sensorToProPhoto));
+        Allocation output = Allocation.createFromBitmap(rs, argbOutput);
+
+        float[] proPhotoToSRGB = new float[9];
+        multiply(sXYZtoRGBBradford, sProPhotoToXYZ, /*out*/proPhotoToSRGB);
+
+        // Setup input allocation (16-bit raw pixels)
+        Type.Builder typeBuilder = new Type.Builder(rs, Element.U16(rs));
+        typeBuilder.setX((inputStride / 2));
+        typeBuilder.setY(inputHeight);
+        Type inputType = typeBuilder.create();
+        Allocation input = Allocation.createTyped(rs, inputType);
+        input.copyFromUnchecked(rawImageInput);
+
+        // Setup RS kernel globals
+        ScriptC_raw_converter converterKernel = new ScriptC_raw_converter(rs);
+        converterKernel.set_inputRawBuffer(input);
+        converterKernel.set_whiteLevel(whiteLevel);
+        converterKernel.set_sensorToIntermediate(new Matrix3f(transpose(sensorToProPhoto)));
+        converterKernel.set_intermediateToSRGB(new Matrix3f(transpose(proPhotoToSRGB)));
+        converterKernel.set_offsetX(outputOffsetX);
+        converterKernel.set_offsetY(outputOffsetY);
+        converterKernel.set_rawHeight(inputHeight);
+        converterKernel.set_rawWidth(inputWidth);
+        converterKernel.set_neutralPoint(new Float3(neutralColorPoint[0].floatValue(),
+                neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()));
+        converterKernel.set_toneMapCoeffs(new Float4(DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[0],
+                DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[1], DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[2],
+                DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[3]));
+        converterKernel.set_hasGainMap(gainMap != null);
+        if (gainMap != null) {
+            converterKernel.set_gainMap(gainMap);
+            converterKernel.set_gainMapWidth(lensShadingMap.getColumnCount());
+            converterKernel.set_gainMapHeight(lensShadingMap.getRowCount());
+        }
+
+        converterKernel.set_cfaPattern(cfa);
+        converterKernel.set_blackLevelPattern(new Int4(blackLevelPattern[0],
+                blackLevelPattern[1], blackLevelPattern[2], blackLevelPattern[3]));
+        converterKernel.forEach_convert_RAW_To_ARGB(output);
+        output.copyTo(argbOutput);  // Force RS sync with bitmap (does not do an extra copy).
+    }
+
+    /**
+     * Create a float-backed renderscript {@link Allocation} with the given dimensions, containing
+     * the contents of the given float array.
+     *
+     * @param rs a {@link RenderScript} context to use.
+     * @param fArray the float array to copy into the {@link Allocation}.
+     * @param width the width of the {@link Allocation}.
+     * @param height the height of the {@link Allocation}.
+     * @return an {@link Allocation} containing the given floats.
+     */
+    private static Allocation createFloat4Allocation(RenderScript rs, float[] fArray,
+                                                    int width, int height) {
+        if (fArray.length != width * height * 4) {
+            throw new IllegalArgumentException("Invalid float array of length " + fArray.length +
+                    ", must be correct size for Allocation of dimensions " + width + "x" + height);
+        }
+        Type.Builder builder = new Type.Builder(rs, Element.F32_4(rs));
+        builder.setX(width);
+        builder.setY(height);
+        Allocation fAlloc = Allocation.createTyped(rs, builder.create());
+        fAlloc.copyFrom(fArray);
+        return fAlloc;
+    }
+
+    /**
+     * Calculate the correlated color temperature (CCT) for a given x,y chromaticity in CIE 1931 x,y
+     * chromaticity space using McCamy's cubic approximation algorithm given in:
+     *
+     * McCamy, Calvin S. (April 1992).
+     * "Correlated color temperature as an explicit function of chromaticity coordinates".
+     * Color Research & Application 17 (2): 142–144
+     *
+     * @param x x chromaticity component.
+     * @param y y chromaticity component.
+     *
+     * @return the CCT associated with this chromaticity coordinate.
+     */
+    private static double calculateColorTemperature(double x, double y) {
+        double n = (x - 0.332) / (y - 0.1858);
+        return -449 * Math.pow(n, 3) + 3525 * Math.pow(n, 2) - 6823.3 * n + 5520.33;
+    }
+
+    /**
+     * Calculate the x,y chromaticity coordinates in CIE 1931 x,y chromaticity space from the given
+     * CIE XYZ coordinates.
+     *
+     * @param X the CIE XYZ X coordinate.
+     * @param Y the CIE XYZ Y coordinate.
+     * @param Z the CIE XYZ Z coordinate.
+     *
+     * @return the [x, y] chromaticity coordinates as doubles.
+     */
+    private static double[] calculateCIExyCoordinates(double X, double Y, double Z) {
+        double[] ret = new double[] { 0, 0 };
+        ret[0] = X / (X + Y + Z);
+        ret[1] = Y / (X + Y + Z);
+        return ret;
+    }
+
+    /**
+     * Linearly interpolate between a and b given fraction f.
+     *
+     * @param a first term to interpolate between, a will be returned when f == 0.
+     * @param b second term to interpolate between, b will be returned when f == 1.
+     * @param f the fraction to interpolate by.
+     *
+     * @return interpolated result as double.
+     */
+    private static double lerp(double a, double b, double f) {
+        return (a * (1.0f - f)) + (b * f);
+    }
+
+    /**
+     * Linearly interpolate between 3x3 matrices a and b given fraction f.
+     *
+     * @param a first 3x3 matrix to interpolate between, a will be returned when f == 0.
+     * @param b second 3x3 matrix to interpolate between, b will be returned when f == 1.
+     * @param f the fraction to interpolate by.
+     * @param result will be set to contain the interpolated matrix.
+     */
+    private static void lerp(float[] a, float[] b, double f, /*out*/float[] result) {
+        for (int i = 0; i < 9; i++) {
+            result[i] = (float) lerp(a[i], b[i], f);
+        }
+    }
+
+    /**
+     * Convert a 9x9 {@link ColorSpaceTransform} to a matrix and write the matrix into the
+     * output.
+     *
+     * @param xform a {@link ColorSpaceTransform} to transform.
+     * @param output the 3x3 matrix to overwrite.
+     */
+    private static void convertColorspaceTransform(ColorSpaceTransform xform, /*out*/float[] output) {
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                output[i * 3 + j] = xform.getElement(j, i).floatValue();
+            }
+        }
+    }
+
+    /**
+     * Find the interpolation factor to use with the RAW matrices given a neutral color point.
+     *
+     * @param referenceIlluminant1 first reference illuminant.
+     * @param referenceIlluminant2 second reference illuminant.
+     * @param calibrationTransform1 calibration matrix corresponding to the first reference
+     *                              illuminant.
+     * @param calibrationTransform2 calibration matrix corresponding to the second reference
+     *                              illuminant.
+     * @param colorMatrix1 color matrix corresponding to the first reference illuminant.
+     * @param colorMatrix2 color matrix corresponding to the second reference illuminant.
+     * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+     *
+     * @return the interpolation factor corresponding to the given neutral color point.
+     */
+    private static double findDngInterpolationFactor(int referenceIlluminant1,
+            int referenceIlluminant2, float[] calibrationTransform1, float[] calibrationTransform2,
+            float[] colorMatrix1, float[] colorMatrix2, Rational[/*3*/] neutralColorPoint) {
+
+        int colorTemperature1 = sStandardIlluminants.get(referenceIlluminant1, NO_ILLUMINANT);
+        if (colorTemperature1 == NO_ILLUMINANT) {
+            throw new IllegalArgumentException("No such illuminant for reference illuminant 1: " +
+                    referenceIlluminant1);
+        }
+
+        int colorTemperature2 = sStandardIlluminants.get(referenceIlluminant2, NO_ILLUMINANT);
+        if (colorTemperature2 == NO_ILLUMINANT) {
+            throw new IllegalArgumentException("No such illuminant for reference illuminant 2: " +
+                    referenceIlluminant2);
+        }
+
+        if (DEBUG) Log.d(TAG, "ColorTemperature1: " + colorTemperature1);
+        if (DEBUG) Log.d(TAG, "ColorTemperature2: " + colorTemperature2);
+
+        double interpFactor = 0.5; // Initial guess for interpolation factor
+        double oldInterpFactor = interpFactor;
+
+        double lastDiff = Double.MAX_VALUE;
+        double tolerance = 0.0001;
+        float[] XYZToCamera1 = new float[9];
+        float[] XYZToCamera2 = new float[9];
+        multiply(calibrationTransform1, colorMatrix1, /*out*/XYZToCamera1);
+        multiply(calibrationTransform2, colorMatrix2, /*out*/XYZToCamera2);
+
+        float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+                neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+
+        float[] neutralGuess = new float[3];
+        float[] interpXYZToCamera = new float[9];
+        float[] interpXYZToCameraInverse = new float[9];
+
+
+        double lower = Math.min(colorTemperature1, colorTemperature2);
+        double upper = Math.max(colorTemperature1, colorTemperature2);
+
+        if(DEBUG) {
+            Log.d(TAG, "XYZtoCamera1: " + Arrays.toString(XYZToCamera1));
+            Log.d(TAG, "XYZtoCamera2: " + Arrays.toString(XYZToCamera2));
+            Log.d(TAG, "Finding interpolation factor, initial guess 0.5...");
+        }
+        // Iteratively guess xy value, find new CCT, and update interpolation factor.
+        int loopLimit = 30;
+        int count = 0;
+        while (lastDiff > tolerance && loopLimit > 0) {
+            if (DEBUG) Log.d(TAG, "Loop count " + count);
+            lerp(XYZToCamera1, XYZToCamera2, interpFactor, interpXYZToCamera);
+            if (!invert(interpXYZToCamera, /*out*/interpXYZToCameraInverse)) {
+                throw new IllegalArgumentException(
+                        "Cannot invert XYZ to Camera matrix, input matrices are invalid.");
+            }
+
+            map(interpXYZToCameraInverse, cameraNeutral, /*out*/neutralGuess);
+            double[] xy = calculateCIExyCoordinates(neutralGuess[0], neutralGuess[1],
+                    neutralGuess[2]);
+
+            double colorTemperature = calculateColorTemperature(xy[0], xy[1]);
+
+            if (colorTemperature <= lower) {
+                interpFactor = 1;
+            } else if (colorTemperature >= upper) {
+                interpFactor = 0;
+            } else {
+                double invCT = 1.0 / colorTemperature;
+                interpFactor = (invCT - 1.0 / upper) / ( 1.0 / lower - 1.0 / upper);
+            }
+
+            if (lower == colorTemperature1) {
+                interpFactor = 1.0 - interpFactor;
+            }
+
+            interpFactor = (interpFactor + oldInterpFactor) / 2;
+            lastDiff = Math.abs(oldInterpFactor - interpFactor);
+            oldInterpFactor = interpFactor;
+            loopLimit--;
+            count++;
+
+            if (DEBUG) {
+                Log.d(TAG, "CameraToXYZ chosen: " + Arrays.toString(interpXYZToCameraInverse));
+                Log.d(TAG, "XYZ neutral color guess: " + Arrays.toString(neutralGuess));
+                Log.d(TAG, "xy coordinate: " + Arrays.toString(xy));
+                Log.d(TAG, "xy color temperature: " + colorTemperature);
+                Log.d(TAG, "New interpolation factor: " + interpFactor);
+            }
+        }
+
+        if (loopLimit == 0) {
+            Log.w(TAG, "Could not converge on interpolation factor, using factor " + interpFactor +
+                    " with remaining error factor of " + lastDiff);
+        }
+        return interpFactor;
+    }
+
+    /**
+     * Calculate the transform from the raw camera sensor colorspace to CIE XYZ colorspace with a
+     * D50 whitepoint.
+     *
+     * @param forwardTransform1 forward transform matrix corresponding to the first reference
+     *                          illuminant.
+     * @param forwardTransform2 forward transform matrix corresponding to the second reference
+     *                          illuminant.
+     * @param calibrationTransform1 calibration transform matrix corresponding to the first
+     *                              reference illuminant.
+     * @param calibrationTransform2 calibration transform matrix corresponding to the second
+     *                              reference illuminant.
+     * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+     * @param interpolationFactor the interpolation factor to use for the forward and
+     *                            calibration transforms.
+     * @param outputTransform set to the full sensor to XYZ colorspace transform.
+     */
+    private static void calculateCameraToXYZD50Transform(float[] forwardTransform1,
+            float[] forwardTransform2, float[] calibrationTransform1, float[] calibrationTransform2,
+            Rational[/*3*/] neutralColorPoint, double interpolationFactor,
+            /*out*/float[] outputTransform) {
+        float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+                neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+        if (DEBUG) Log.d(TAG, "Camera neutral: " + Arrays.toString(cameraNeutral));
+
+        float[] interpolatedCC = new float[9];
+        lerp(calibrationTransform1, calibrationTransform2, interpolationFactor,
+                interpolatedCC);
+        float[] inverseInterpolatedCC = new float[9];
+        if (!invert(interpolatedCC, /*out*/inverseInterpolatedCC)) {
+            throw new IllegalArgumentException( "Cannot invert interpolated calibration transform" +
+                    ", input matrices are invalid.");
+        }
+        if (DEBUG) Log.d(TAG, "Inverted interpolated CalibrationTransform: " +
+                Arrays.toString(inverseInterpolatedCC));
+
+        float[] referenceNeutral = new float[3];
+        map(inverseInterpolatedCC, cameraNeutral, /*out*/referenceNeutral);
+        if (DEBUG) Log.d(TAG, "Reference neutral: " + Arrays.toString(referenceNeutral));
+        float maxNeutral = Math.max(Math.max(referenceNeutral[0], referenceNeutral[1]),
+                referenceNeutral[2]);
+        float[] D = new float[] { maxNeutral/referenceNeutral[0], 0, 0,
+                                  0, maxNeutral/referenceNeutral[1], 0,
+                                  0, 0, maxNeutral/referenceNeutral[2] };
+        if (DEBUG) Log.d(TAG, "Reference Neutral Diagonal: " + Arrays.toString(D));
+
+        float[] intermediate = new float[9];
+        float[] intermediate2 = new float[9];
+
+        lerp(forwardTransform1, forwardTransform2, interpolationFactor, /*out*/intermediate);
+        if (DEBUG) Log.d(TAG, "Interpolated ForwardTransform: " + Arrays.toString(intermediate));
+
+        multiply(D, inverseInterpolatedCC, /*out*/intermediate2);
+        multiply(intermediate, intermediate2, /*out*/outputTransform);
+    }
+
+    /**
+     * Map a 3d column vector using the given matrix.
+     *
+     * @param matrix float array containing 3x3 matrix to map vector by.
+     * @param input 3 dimensional vector to map.
+     * @param output 3 dimensional vector result.
+     */
+    private static void map(float[] matrix, float[] input, /*out*/float[] output) {
+        output[0] = input[0] * matrix[0] + input[1] * matrix[1] + input[2] * matrix[2];
+        output[1] = input[0] * matrix[3] + input[1] * matrix[4] + input[2] * matrix[5];
+        output[2] = input[0] * matrix[6] + input[1] * matrix[7] + input[2] * matrix[8];
+    }
+
+    /**
+     * Multiply two 3x3 matrices together: A * B
+     *
+     * @param a left matrix.
+     * @param b right matrix.
+     */
+    private static void multiply(float[] a, float[] b, /*out*/float[] output) {
+        output[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
+        output[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
+        output[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
+        output[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
+        output[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
+        output[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
+        output[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
+        output[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
+        output[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
+    }
+
+    /**
+     * Transpose a 3x3 matrix in-place.
+     *
+     * @param m the matrix to transpose.
+     * @return the transposed matrix.
+     */
+    private static float[] transpose(/*inout*/float[/*9*/] m) {
+        float t = m[1];
+        m[1] = m[3];
+        m[3] = t;
+        t = m[2];
+        m[2] = m[6];
+        m[6] = t;
+        t = m[5];
+        m[5] = m[7];
+        m[7] = t;
+        return m;
+    }
+
+    /**
+     * Invert a 3x3 matrix, or return false if the matrix is singular.
+     *
+     * @param m matrix to invert.
+     * @param output set the output to be the inverse of m.
+     */
+    private static boolean invert(float[] m, /*out*/float[] output) {
+        double a00 = m[0];
+        double a01 = m[1];
+        double a02 = m[2];
+        double a10 = m[3];
+        double a11 = m[4];
+        double a12 = m[5];
+        double a20 = m[6];
+        double a21 = m[7];
+        double a22 = m[8];
+
+        double t00 = a11 * a22 - a21 * a12;
+        double t01 = a21 * a02 - a01 * a22;
+        double t02 = a01 * a12 - a11 * a02;
+        double t10 = a20 * a12 - a10 * a22;
+        double t11 = a00 * a22 - a20 * a02;
+        double t12 = a10 * a02 - a00 * a12;
+        double t20 = a10 * a21 - a20 * a11;
+        double t21 = a20 * a01 - a00 * a21;
+        double t22 = a00 * a11 - a10 * a01;
+
+        double det = a00 * t00 + a01 * t10 + a02 * t20;
+        if (Math.abs(det) < 1e-9) {
+            return false; // Inverse too close to zero, not invertible.
+        }
+
+        output[0] = (float) (t00 / det);
+        output[1] = (float) (t01 / det);
+        output[2] = (float) (t02 / det);
+        output[3] = (float) (t10 / det);
+        output[4] = (float) (t11 / det);
+        output[5] = (float) (t12 / det);
+        output[6] = (float) (t20 / det);
+        output[7] = (float) (t21 / det);
+        output[8] = (float) (t22 / det);
+        return true;
+    }
+
+    /**
+     * Scale each element in a matrix by the given scaling factor.
+     *
+     * @param factor factor to scale by.
+     * @param matrix the float array containing a 3x3 matrix to scale.
+     */
+    private static void scale(float factor, /*inout*/float[] matrix) {
+        for (int i = 0; i < 9; i++) {
+            matrix[i] *= factor;
+        }
+    }
+
+    /**
+     * Clamp a value to a given range.
+     *
+     * @param low lower bound to clamp to.
+     * @param high higher bound to clamp to.
+     * @param value the value to clamp.
+     * @return the clamped value.
+     */
+    private static double clamp(double low, double high, double value) {
+        return Math.max(low, Math.min(high, value));
+    }
+
+    /**
+     * Return the max float in the array.
+     *
+     * @param array array of floats to search.
+     * @return max float in the array.
+     */
+    private static float max(float[] array) {
+        float val = array[0];
+        for (float f : array) {
+            val = (f > val) ? f : val;
+        }
+        return val;
+    }
+
+    /**
+     * Normalize ColorMatrix to eliminate headroom for input space scaled to [0, 1] using
+     * the D50 whitepoint.  This maps the D50 whitepoint into the colorspace used by the
+     * ColorMatrix, then uses the resulting whitepoint to renormalize the ColorMatrix so
+     * that the channel values in the resulting whitepoint for this operation are clamped
+     * to the range [0, 1].
+     *
+     * @param colorMatrix a 3x3 matrix containing a DNG ColorMatrix to be normalized.
+     */
+    private static void normalizeCM(/*inout*/float[] colorMatrix) {
+        float[] tmp = new float[3];
+        map(colorMatrix, D50_XYZ, /*out*/tmp);
+        float maxVal = max(tmp);
+        if (maxVal > 0) {
+            scale(1.0f / maxVal, colorMatrix);
+        }
+    }
+
+    /**
+     * Normalize ForwardMatrix to ensure that sensor whitepoint [1, 1, 1] maps to D50 in CIE XYZ
+     * colorspace.
+     *
+     * @param forwardMatrix a 3x3 matrix containing a DNG ForwardTransform to be normalized.
+     */
+    private static void normalizeFM(/*inout*/float[] forwardMatrix) {
+        float[] tmp = new float[] {1, 1, 1};
+        float[] xyz = new float[3];
+        map(forwardMatrix, tmp, /*out*/xyz);
+
+        float[] intermediate = new float[9];
+        float[] m = new float[] {1.0f / xyz[0], 0, 0, 0, 1.0f / xyz[1], 0, 0, 0, 1.0f / xyz[2]};
+
+        multiply(m, forwardMatrix, /*out*/ intermediate);
+        float[] m2 = new float[] {D50_XYZ[0], 0, 0, 0, D50_XYZ[1], 0, 0, 0, D50_XYZ[2]};
+        multiply(m2, intermediate, /*out*/forwardMatrix);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
new file mode 100644
index 0000000..c8b353e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
@@ -0,0 +1,369 @@
+/*
+ * 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.
+ */
+
+#include "../common.rs"
+
+// This file includes a conversion kernel for RGGB, GRBG, GBRG, and BGGR Bayer patterns.
+// Applying this script also will apply black-level subtraction, rescaling, clipping, tonemapping,
+// and color space transforms along with the Bayer demosaic.  See RawConverter.java
+// for more information.
+
+// Input globals
+
+rs_allocation inputRawBuffer; // RAW16 buffer of dimensions (raw image stride) * (raw image height)
+rs_allocation gainMap; // Gainmap to apply to linearized raw sensor data.
+uint cfaPattern; // The Color Filter Arrangement pattern used
+uint gainMapWidth;  // The width of the gain map
+uint gainMapHeight;  // The height of the gain map
+bool hasGainMap; // Does gainmap exist?
+rs_matrix3x3 sensorToIntermediate; // Color transform from sensor to a wide-gamut colorspace
+rs_matrix3x3 intermediateToSRGB; // Color transform from wide-gamut colorspace to sRGB
+ushort4 blackLevelPattern; // Blacklevel to subtract for each channel, given in CFA order
+int whiteLevel;  // Whitelevel of sensor
+uint offsetX; // X offset into inputRawBuffer
+uint offsetY; // Y offset into inputRawBuffer
+uint rawWidth; // Width of raw buffer
+uint rawHeight; // Height of raw buffer
+float3 neutralPoint; // The camera neutral
+float4 toneMapCoeffs; // Coefficients for a polynomial tonemapping curve
+
+// Interpolate gain map to find per-channel gains at a given pixel
+static float4 getGain(uint x, uint y) {
+    float interpX = (((float) x) / rawWidth) * gainMapWidth;
+    float interpY = (((float) y) / rawHeight) * gainMapHeight;
+    uint gX = (uint) interpX;
+    uint gY = (uint) interpY;
+    uint gXNext = (gX + 1 < gainMapWidth) ? gX + 1 : gX;
+    uint gYNext = (gY + 1 < gainMapHeight) ? gY + 1 : gY;
+
+    float4 tl = *((float4 *) rsGetElementAt(gainMap, gX, gY));
+    float4 tr = *((float4 *) rsGetElementAt(gainMap, gXNext, gY));
+    float4 bl = *((float4 *) rsGetElementAt(gainMap, gX, gYNext));
+    float4 br = *((float4 *) rsGetElementAt(gainMap, gXNext, gYNext));
+
+    float fracX = interpX - (float) gX;
+    float fracY = interpY - (float) gY;
+    float invFracX = 1.f - fracX;
+    float invFracY = 1.f - fracY;
+
+    return tl * invFracX * invFracY + tr * fracX * invFracY +
+            bl * invFracX * fracY + br * fracX * fracY;
+}
+
+// Apply gamma correction using sRGB gamma curve
+static float gammaEncode(float x) {
+    return (x <= 0.0031308f) ? x * 12.92f : 1.055f * pow(x, 0.4166667f) - 0.055f;
+}
+
+// Apply gamma correction to each color channel in RGB pixel
+static float3 gammaCorrectPixel(float3 rgb) {
+    float3 ret;
+    ret.x = gammaEncode(rgb.x);
+    ret.y = gammaEncode(rgb.y);
+    ret.z = gammaEncode(rgb.z);
+    return ret;
+}
+
+// Apply polynomial tonemapping curve to each color channel in RGB pixel.
+// This attempts to apply tonemapping without changing the hue of each pixel,
+// i.e.:
+//
+// For some RGB values:
+// M = max(R, G, B)
+// m = min(R, G, B)
+// m' = mid(R, G, B)
+// chroma = M - m
+// H = m' - m / chroma
+//
+// The relationship H=H' should be preserved, where H and H' are calculated from
+// the RGB and RGB' value at this pixel before and after this tonemapping
+// operation has been applied, respectively.
+static float3 tonemap(float3 rgb) {
+    float3 sorted = clamp(rgb, 0.f, 1.f);
+    float tmp;
+    int permutation = 0;
+
+    // Sort the RGB channels by value
+    if (sorted.z < sorted.y) {
+        tmp = sorted.z;
+        sorted.z = sorted.y;
+        sorted.y = tmp;
+        permutation |= 1;
+    }
+    if (sorted.y < sorted.x) {
+        tmp = sorted.y;
+        sorted.y = sorted.x;
+        sorted.x = tmp;
+        permutation |= 2;
+    }
+    if (sorted.z < sorted.y) {
+        tmp = sorted.z;
+        sorted.z = sorted.y;
+        sorted.y = tmp;
+        permutation |= 4;
+    }
+
+    float2 minmax;
+    minmax.x = sorted.x;
+    minmax.y = sorted.z;
+
+    // Apply tonemapping curve to min, max RGB channel values
+    minmax = native_powr(minmax, 3.f) * toneMapCoeffs.x +
+            native_powr(minmax, 2.f) * toneMapCoeffs.y +
+            minmax * toneMapCoeffs.z + toneMapCoeffs.w;
+
+    // Rescale middle value
+    float newMid;
+    if (sorted.z == sorted.x) {
+        newMid = minmax.y;
+    } else {
+        newMid = minmax.x + ((minmax.y - minmax.x) * (sorted.y - sorted.x) /
+                (sorted.z - sorted.x));
+    }
+
+    float3 finalRGB;
+    switch (permutation) {
+        case 0: // b >= g >= r
+            finalRGB.x = minmax.x;
+            finalRGB.y = newMid;
+            finalRGB.z = minmax.y;
+            break;
+        case 1: // g >= b >= r
+            finalRGB.x = minmax.x;
+            finalRGB.z = newMid;
+            finalRGB.y = minmax.y;
+            break;
+        case 2: // b >= r >= g
+            finalRGB.y = minmax.x;
+            finalRGB.x = newMid;
+            finalRGB.z = minmax.y;
+            break;
+        case 3: // g >= r >= b
+            finalRGB.z = minmax.x;
+            finalRGB.x = newMid;
+            finalRGB.y = minmax.y;
+            break;
+        case 6: // r >= b >= g
+            finalRGB.y = minmax.x;
+            finalRGB.z = newMid;
+            finalRGB.x = minmax.y;
+            break;
+        case 7: // r >= g >= b
+            finalRGB.z = minmax.x;
+            finalRGB.y = newMid;
+            finalRGB.x = minmax.y;
+            break;
+        case 4: // impossible
+        case 5: // impossible
+        default:
+            LOGD("raw_converter.rs: Logic error in tonemap.", 0);
+            break;
+    }
+    return clamp(finalRGB, 0.f, 1.f);
+}
+
+// Apply a colorspace transform to the intermediate colorspace, apply
+// a tonemapping curve, apply a colorspace transform to a final colorspace,
+// and apply a gamma correction curve.
+static float3 applyColorspace(float3 pRGB) {
+    pRGB.x = clamp(pRGB.x, 0.f, neutralPoint.x);
+    pRGB.y = clamp(pRGB.y, 0.f, neutralPoint.y);
+    pRGB.z = clamp(pRGB.z, 0.f, neutralPoint.z);
+
+    float3 intermediate = rsMatrixMultiply(&sensorToIntermediate, pRGB);
+    intermediate = tonemap(intermediate);
+    return gammaCorrectPixel(clamp(rsMatrixMultiply(&intermediateToSRGB, intermediate), 0.f, 1.f));
+}
+
+// Load a 3x3 patch of pixels into the output.
+static void load3x3(uint x, uint y, rs_allocation buf, /*out*/float* outputArray) {
+    outputArray[0] = *((ushort *) rsGetElementAt(buf, x - 1, y - 1));
+    outputArray[1] = *((ushort *) rsGetElementAt(buf, x, y - 1));
+    outputArray[2] = *((ushort *) rsGetElementAt(buf, x + 1, y - 1));
+    outputArray[3] = *((ushort *) rsGetElementAt(buf, x - 1, y));
+    outputArray[4] = *((ushort *) rsGetElementAt(buf, x, y));
+    outputArray[5] = *((ushort *) rsGetElementAt(buf, x + 1, y));
+    outputArray[6] = *((ushort *) rsGetElementAt(buf, x - 1, y + 1));
+    outputArray[7] = *((ushort *) rsGetElementAt(buf, x, y + 1));
+    outputArray[8] = *((ushort *) rsGetElementAt(buf, x + 1, y + 1));
+}
+
+// Blacklevel subtract, and normalize each pixel in the outputArray, and apply the
+// gain map.
+static void linearizeAndGainmap(uint x, uint y, ushort4 blackLevel, int whiteLevel,
+        uint cfa, /*inout*/float* outputArray) {
+    uint kk = 0;
+    for (uint j = y - 1; j <= y + 1; j++) {
+        for (uint i = x - 1; i <= x + 1; i++) {
+            uint index = (i & 1) | ((j & 1) << 1);  // bits [0,1] are blacklevel offset
+            index |= (cfa << 2);  // bits [2,3] are cfa
+            float bl = 0.f;
+            float g = 1.f;
+            float4 gains = 1.f;
+            if (hasGainMap) {
+                gains = getGain(i, j);
+            }
+            switch (index) {
+                // RGGB
+                case 0:
+                    bl = blackLevel.x;
+                    g = gains.x;
+                    break;
+                case 1:
+                    bl = blackLevel.y;
+                    g = gains.y;
+                    break;
+                case 2:
+                    bl = blackLevel.z;
+                    g = gains.z;
+                    break;
+                case 3:
+                    bl = blackLevel.w;
+                    g = gains.w;
+                    break;
+                // GRBG
+                case 4:
+                    bl = blackLevel.x;
+                    g = gains.y;
+                    break;
+                case 5:
+                    bl = blackLevel.y;
+                    g = gains.x;
+                    break;
+                case 6:
+                    bl = blackLevel.z;
+                    g = gains.w;
+                    break;
+                case 7:
+                    bl = blackLevel.w;
+                    g = gains.z;
+                    break;
+                // GBRG
+                case 8:
+                    bl = blackLevel.x;
+                    g = gains.y;
+                    break;
+                case 9:
+                    bl = blackLevel.y;
+                    g = gains.w;
+                    break;
+                case 10:
+                    bl = blackLevel.z;
+                    g = gains.x;
+                    break;
+                case 11:
+                    bl = blackLevel.w;
+                    g = gains.z;
+                    break;
+                // BGGR
+                case 12:
+                    bl = blackLevel.x;
+                    g = gains.w;
+                    break;
+                case 13:
+                    bl = blackLevel.y;
+                    g = gains.y;
+                    break;
+                case 14:
+                    bl = blackLevel.z;
+                    g = gains.z;
+                    break;
+                case 15:
+                    bl = blackLevel.w;
+                    g = gains.x;
+                    break;
+            }
+            outputArray[kk] = clamp(g * (outputArray[kk] - bl) / (whiteLevel - bl), 0.f, 1.f);
+            kk++;
+        }
+    }
+}
+
+// Apply bilinear-interpolation to demosaic
+static float3 demosaic(uint x, uint y, uint cfa, float* inputArray) {
+    uint index = (x & 1) | ((y & 1) << 1);
+    index |= (cfa << 2);
+    float3 pRGB;
+    switch (index) {
+        case 0:
+        case 5:
+        case 10:
+        case 15:  // Red centered
+                  // B G B
+                  // G R G
+                  // B G B
+            pRGB.x = inputArray[4];
+            pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+            pRGB.z = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+            break;
+        case 1:
+        case 4:
+        case 11:
+        case 14: // Green centered w/ horizontally adjacent Red
+                 // G B G
+                 // R G R
+                 // G B G
+            pRGB.x = (inputArray[3] + inputArray[5]) / 2;
+            pRGB.y = inputArray[4];
+            pRGB.z = (inputArray[1] + inputArray[7]) / 2;
+            break;
+        case 2:
+        case 7:
+        case 8:
+        case 13: // Green centered w/ horizontally adjacent Blue
+                 // G R G
+                 // B G B
+                 // G R G
+            pRGB.x = (inputArray[1] + inputArray[7]) / 2;
+            pRGB.y = inputArray[4];
+            pRGB.z = (inputArray[3] + inputArray[5]) / 2;
+            break;
+        case 3:
+        case 6:
+        case 9:
+        case 12: // Blue centered
+                 // R G R
+                 // G B G
+                 // R G R
+            pRGB.x = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+            pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+            pRGB.z = inputArray[4];
+            break;
+    }
+
+    return pRGB;
+}
+
+// Full RAW->ARGB bitmap conversion kernel
+uchar4 RS_KERNEL convert_RAW_To_ARGB(uint x, uint y) {
+    float3 pRGB;
+    uint xP = x + offsetX;
+    uint yP = y + offsetY;
+    if (xP == 0) xP = 1;
+    if (yP == 0) yP = 1;
+    if (xP == rawWidth - 1) xP = rawWidth - 2;
+    if (yP == rawHeight - 1) yP = rawHeight  - 2;
+
+    float patch[9];
+    // TODO: Once ScriptGroup and RS kernels have been updated to allow for iteration over 3x3 pixel
+    // patches, this can be optimized to avoid re-applying the pre-demosaic steps for each pixel,
+    // potentially achieving a 9x speedup here.
+    load3x3(xP, yP, inputRawBuffer, /*out*/ patch);
+    linearizeAndGainmap(xP, yP, blackLevelPattern, whiteLevel, cfaPattern, /*inout*/patch);
+    pRGB = demosaic(xP, yP, cfaPattern, patch);
+
+    return rsPackColorTo8888(applyColorspace(pRGB));
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 5fc6321..ff69581 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -38,6 +38,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.view.Surface;
+import android.view.WindowManager;
 
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
@@ -70,11 +71,14 @@
     protected List<Size> mOrderedVideoSizes; // In descending order.
     protected List<Size> mOrderedStillSizes; // In descending order.
 
+    protected WindowManager mWindowManager;
+
     @Override
     public void setContext(Context context) {
         super.setContext(context);
         mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
         assertNotNull("Can't connect to camera manager!", mCameraManager);
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }
 
     /**
@@ -184,7 +188,8 @@
         mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
                 CheckLevel.ASSERT, /*collector*/null);
         mOrderedPreviewSizes = getSupportedPreviewSizes(
-                cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+                cameraId, mCameraManager,
+                getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
         mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
         mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index 03e9647..5d832d6 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -43,6 +43,7 @@
 import android.util.Size;
 import android.view.Surface;
 import android.view.TextureView;
+import android.view.WindowManager;
 
 import com.android.ex.camera2.blocking.BlockingCameraManager;
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
@@ -76,6 +77,8 @@
     private CameraHolder[] mCameraHolders;
     private HashMap<String, Integer> mCameraIdMap;
 
+    protected WindowManager mWindowManager;
+
     public Camera2MultiViewTestCase() {
         super(Camera2MultiViewCtsActivity.class);
     }
@@ -104,6 +107,7 @@
             mCameraHolders[i] = new CameraHolder(mCameraIds[i]);
             mCameraIdMap.put(mCameraIds[i], i);
         }
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }
 
     @Override
@@ -359,7 +363,8 @@
             mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraId),
                     CheckLevel.ASSERT, /*collector*/null);
             mOrderedPreviewSizes = getSupportedPreviewSizes(
-                    mCameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+                    mCameraId, mCameraManager,
+                    getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
             assertNotNull(String.format("Failed to open camera device ID: %s", mCameraId), mCamera);
         }
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index bcc4061..00f7e1c 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceHolder;
+import android.view.WindowManager;
 import android.content.Context;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraAccessException;
@@ -102,6 +103,7 @@
     protected List<Size> mOrderedStillSizes; // In descending order.
     protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
 
+    protected WindowManager mWindowManager;
 
     public Camera2SurfaceViewTestCase() {
         super(Camera2SurfaceViewCtsActivity.class);
@@ -131,6 +133,8 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateCallback();
         mCollector = new CameraErrorCollector();
+
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }
 
     @Override
@@ -559,7 +563,8 @@
         mCollector.setCameraId(cameraId);
         mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
                 CheckLevel.ASSERT, /*collector*/null);
-        mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
+                getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
         mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
         mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
         // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
@@ -612,8 +617,8 @@
                 mPreviewSize.getHeight());
         assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
         mPreviewSurface = holder.getSurface();
-        assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
         assertNotNull("Preview surface is null", mPreviewSurface);
+        assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
index d8f8a1d..380e47d 100755
--- a/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
@@ -497,7 +497,7 @@
      * TODO: This should be made stricter once SurfaceTexture timestamps are generated by the drivers.
      */
     @UiThreadTest
-    @TimeoutReq(minutes = 20)
+    @TimeoutReq(minutes = 30)
     public void testCameraToSurfaceTextureMetadata() throws Exception {
         runForAllCameras(testCameraToSurfaceTextureMetadataByCamera);
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index 019bd21..cf616b6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -37,6 +37,7 @@
 import android.os.ConditionVariable;
 import android.os.Environment;
 import android.os.Looper;
+import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.MoreAsserts;
 import android.test.UiThreadTest;
@@ -44,6 +45,7 @@
 import android.util.Log;
 import android.view.SurfaceHolder;
 
+import com.android.cts.util.TimeoutReq;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -860,15 +862,16 @@
     }
 
     private void testJpegExifByCamera(boolean recording) throws Exception {
-        Camera.Parameters parameters = mCamera.getParameters();
         if (!recording) mCamera.startPreview();
-        double focalLength = parameters.getFocalLength();
         Date date = new Date(System.currentTimeMillis());
         String localDatetime = new SimpleDateFormat("yyyy:MM:dd HH:").format(date);
 
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
 
+        Camera.Parameters parameters = mCamera.getParameters();
+        double focalLength = parameters.getFocalLength();
+
         // Test various exif tags.
         ExifInterface exif = new ExifInterface(JPEG_PATH);
         StringBuffer failedCause = new StringBuffer("Jpeg exif test failed:\n");
@@ -1768,6 +1771,7 @@
     }
 
     @UiThreadTest
+    @TimeoutReq(minutes = 30)
     public void testPreviewPictureSizesCombination() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -1975,7 +1979,7 @@
         // This method tests if the actual fps is between minimum and maximum.
         // It also tests if the frame interval is too long.
         public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
-            long arrivalTime = System.currentTimeMillis();
+            long arrivalTime = SystemClock.elapsedRealtime();
             camera.addCallbackBuffer(data);
             if (firstFrameArrivalTime == 0) firstFrameArrivalTime = arrivalTime;
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
old mode 100644
new mode 100755
index 0e09e8c..68efef0
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -92,17 +92,20 @@
                 lessThanDpi(density, DENSITY_MEDIUM, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
                 lessThanDpi(density, DENSITY_LOW, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
 
-            assertFalse("Device is not expected to be 64-bit", supports64Bit);
-            assertMinMemoryMb(424);
-        } else if (greaterThanDpi(density, DENSITY_XHIGH, screenSize,
+            if (supports64Bit) {
+                assertMinMemoryMb(704);
+            } else {
+                assertMinMemoryMb(424);
+            }
+        } else if (greaterThanDpi(density, DENSITY_560, screenSize,
                 SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
-                greaterThanDpi(density, DENSITY_TV, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
-                greaterThanDpi(density, DENSITY_MEDIUM, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
+                greaterThanDpi(density, DENSITY_400, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
+                greaterThanDpi(density, DENSITY_XHIGH, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
 
             if (supports64Bit) {
-                assertMinMemoryMb(832);
+                assertMinMemoryMb(1824);
             } else {
-                assertMinMemoryMb(512);
+                assertMinMemoryMb(1344);
             }
         } else if (greaterThanDpi(density, DENSITY_400, screenSize,
                 SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
@@ -114,15 +117,15 @@
             } else {
                 assertMinMemoryMb(896);
             }
-        } else if (greaterThanDpi(density, DENSITY_560, screenSize,
+        } else if (greaterThanDpi(density, DENSITY_XHIGH, screenSize,
                 SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
-                greaterThanDpi(density, DENSITY_400, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
-                greaterThanDpi(density, DENSITY_XHIGH, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
+                greaterThanDpi(density, DENSITY_TV, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
+                greaterThanDpi(density, DENSITY_MEDIUM, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
 
             if (supports64Bit) {
-                assertMinMemoryMb(1824);
+                assertMinMemoryMb(832);
             } else {
-                assertMinMemoryMb(1344);
+                assertMinMemoryMb(512);
             }
         }
     }
@@ -152,7 +155,7 @@
     private void assertMinMemoryMb(long minMb) {
 
         long totalMemoryMb = getTotalMemory() / ONE_MEGABYTE;
-        boolean lowRam = totalMemoryMb <= minMb * 1.5;
+        boolean lowRam = totalMemoryMb <= 512;
         boolean lowRamDevice = mActivityManager.isLowRamDevice();
 
         Log.i(TAG, String.format("minMb=%,d", minMb));
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
index 687826c..7640cd7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
@@ -20,9 +20,7 @@
 import android.hardware.SensorManager;
 import android.hardware.cts.helpers.SensorStats;
 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 android.hardware.cts.helpers.sensorverification.ISensorVerification;
 
 import java.util.concurrent.TimeUnit;
@@ -263,7 +261,7 @@
                 rateUs,
                 maxBatchReportLatencyUs);
         TestSensorOperation operation =
-                new TestSensorOperation(environment, testDurationSec, TimeUnit.SECONDS);
+                TestSensorOperation.createOperation(environment, testDurationSec, TimeUnit.SECONDS);
 
         executeTest(environment, operation, false /* flushExpected */);
     }
@@ -279,23 +277,23 @@
                 shouldEmulateSensorUnderLoad(),
                 rateUs,
                 maxBatchReportLatencyUs);
-        TestSensorFlushOperation operation =
-                new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+        TestSensorOperation operation = TestSensorOperation
+                .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
 
         executeTest(environment, operation, true /* flushExpected */);
     }
 
     private void executeTest(
             TestSensorEnvironment environment,
-            VerifiableSensorOperation operation,
+            TestSensorOperation operation,
             boolean flushExpected) throws Throwable {
         operation.addDefaultVerifications();
-        operation.setLogEvents(true);
 
         try {
-            operation.execute();
+            operation.execute(getCurrentTestNode());
         } finally {
-            SensorStats.logStats(TAG, operation.getStats());
+            SensorStats stats = operation.getStats();
+            stats.log(TAG);
 
             String sensorRate;
             if (environment.getRequestedSamplingPeriodUs() == SensorManager.SENSOR_DELAY_FASTEST) {
@@ -311,7 +309,7 @@
                     sensorRate,
                     batching,
                     flush);
-            SensorStats.logStatsToFile(fileName, operation.getStats());
+            stats.logToFile(fileName);
         }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index 50cb12d..8c3fb7a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -18,13 +18,11 @@
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
 import android.hardware.cts.helpers.sensoroperations.RepeatingSensorOperation;
 import android.hardware.cts.helpers.sensoroperations.SequentialSensorOperation;
 import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
 import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
 
 import java.util.Random;
@@ -81,7 +79,7 @@
                     shouldEmulateSensorUnderLoad(),
                     SensorManager.SENSOR_DELAY_FASTEST);
             TestSensorOperation continuousOperation =
-                    new TestSensorOperation(environment, 100 /* eventCount */);
+                    TestSensorOperation.createOperation(environment, 100 /* eventCount */);
             continuousOperation.addVerification(new EventOrderingVerification());
             operation.add(new RepeatingSensorOperation(continuousOperation, ITERATIONS));
 
@@ -93,12 +91,12 @@
                     sensor.getMinDelay(),
                     MAX_REPORTING_LATENCY_US);
             TestSensorOperation batchingOperation =
-                    new TestSensorOperation(batchingEnvironment, 100 /* eventCount */);
+                    TestSensorOperation.createOperation(batchingEnvironment, 100 /* eventCount */);
             batchingOperation.addVerification(new EventOrderingVerification());
             operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
         }
-        operation.execute();
-        SensorStats.logStats(TAG, operation.getStats());
+        operation.execute(getCurrentTestNode());
+        operation.getStats().log(TAG);
     }
 
     /**
@@ -145,7 +143,7 @@
                             generateSamplingRateInUs(sensorType),
                             generateReportLatencyInUs());
                     TestSensorOperation sensorOperation =
-                            new TestSensorOperation(environment, 100 /* eventCount */);
+                            TestSensorOperation.createOperation(environment, 100 /* eventCount */);
                     sensorOperation.addVerification(new EventOrderingVerification());
                     sequentialOperation.add(sensorOperation);
                 }
@@ -153,8 +151,8 @@
             }
         }
 
-        operation.execute();
-        SensorStats.logStats(TAG, operation.getStats());
+        operation.execute(getCurrentTestNode());
+        operation.getStats().log(TAG);
     }
 
     /**
@@ -229,7 +227,7 @@
                 shouldEmulateSensorUnderLoad(),
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation tester =
-                new TestSensorOperation(testerEnvironment, 100 /* event count */);
+                TestSensorOperation.createOperation(testerEnvironment, 100 /* event count */);
         tester.addVerification(new EventOrderingVerification());
 
         TestSensorEnvironment testeeEnvironment = new TestSensorEnvironment(
@@ -237,18 +235,18 @@
                 sensorTypeTestee,
                 shouldEmulateSensorUnderLoad(),
                 SensorManager.SENSOR_DELAY_FASTEST);
-        VerifiableSensorOperation testee =
-                new TestSensorOperation(testeeEnvironment, 100 /* event count */);
+        TestSensorOperation testee =
+                TestSensorOperation.createOperation(testeeEnvironment, 100 /* event count */);
         testee.addVerification(new EventOrderingVerification());
 
         ParallelSensorOperation operation = new ParallelSensorOperation();
         operation.add(tester, testee);
-        operation.execute();
-        SensorStats.logStats(TAG, operation.getStats());
+        operation.execute(getCurrentTestNode());
+        operation.getStats().log(TAG);
 
         testee = testee.clone();
-        testee.execute();
-        SensorStats.logStats(TAG, testee.getStats());
+        testee.execute(getCurrentTestNode());
+        testee.getStats().log(TAG);
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index e8e0f25..a16f16f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -18,6 +18,8 @@
 
 import com.android.cts.util.TimeoutReq;
 
+import junit.framework.Assert;
+
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.Sensor;
@@ -27,6 +29,16 @@
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.PowerManager;
@@ -39,33 +51,49 @@
 import java.util.concurrent.TimeUnit;
 
 public class SensorTest extends SensorTestCase {
-    private SensorManager mSensorManager;
-    private TriggerListener mTriggerListener;
-    private SensorListener mSensorListener;
-    private List<Sensor> mSensorList;
     private static final String TAG = "SensorTest";
+
     // Test only SDK defined sensors. Any sensors with type > 100 are ignored.
     private static final int MAX_OFFICIAL_ANDROID_SENSOR_TYPE = 100;
-    private static final long TIMEOUT_TOLERANCE_US = TimeUnit.SECONDS.toMicros(5);
-    private static final double MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE = 0.9;
+
     private PowerManager.WakeLock mWakeLock;
+    private SensorManager mSensorManager;
+    private NullTriggerEventListener mNullTriggerEventListener;
+    private NullSensorEventListener mNullSensorEventListener;
+    private List<Sensor> mSensorList;
 
     @Override
     protected void setUp() throws Exception {
-        super.setUp();
-        mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
-        mTriggerListener = new TriggerListener();
-        mSensorListener = new SensorListener();
-        mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
-        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+        Context context = getContext();
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        mNullTriggerEventListener = new NullTriggerEventListener();
+        mNullSensorEventListener = new NullSensorEventListener();
+
+        mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        assertNotNull("SensorList was null.", mSensorList);
+        if (mSensorList.isEmpty()) {
+            // several devices will not have sensors, so we need to skip the tests in those cases
+            throw new SensorTestStateNotSupportedException(
+                    "Sensors are not available in the system.");
+        }
+
+        mWakeLock.acquire();
     }
 
+    @Override
+    protected void tearDown(){
+        if (mWakeLock != null && mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
+    }
+
+    @SuppressWarnings("deprecation")
     public void testSensorOperations() {
         // Because we can't know every sensors unit details, so we can't assert
         // get values with specified values.
-        List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
-        assertNotNull(sensors);
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
         boolean hasAccelerometer = getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SENSOR_ACCELEROMETER);
@@ -150,7 +178,6 @@
         }
         assertTrue(sensors.get(0).getName() + " defined as non-wake-up sensor",
                 sensors.get(0).isWakeUpSensor());
-        return;
     }
 
     // Some sensors like proximity, significant motion etc. are defined as wake-up sensors by
@@ -208,371 +235,141 @@
 
     public void testRequestTriggerWithNonTriggerSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.requestTriggerSensor(mTriggerListener, sensor);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
+        boolean  result = mSensorManager.requestTriggerSensor(mNullTriggerEventListener, sensor);
+        assertFalse(result);
     }
 
     public void testCancelTriggerWithNonTriggerSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.cancelTriggerSensor(mTriggerListener, sensor);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
+        boolean result = mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, sensor);
+        assertFalse(result);
     }
 
     public void testRegisterWithTriggerSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.registerListener(mSensorListener, sensor,
-                    SensorManager.SENSOR_DELAY_NORMAL);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
         }
+        boolean result = mSensorManager.registerListener(
+                mNullSensorEventListener,
+                sensor,
+                SensorManager.SENSOR_DELAY_NORMAL);
+        assertFalse(result);
     }
 
     public void testRegisterTwiceWithSameSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.registerListener(mSensorListener, sensor,
-                    SensorManager.SENSOR_DELAY_NORMAL);
-            assertTrue(result);
-            result = mSensorManager.registerListener(mSensorListener, sensor,
-                    SensorManager.SENSOR_DELAY_NORMAL);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
+
+        boolean result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+                SensorManager.SENSOR_DELAY_NORMAL);
+        assertTrue(result);
+
+        result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+                SensorManager.SENSOR_DELAY_NORMAL);
+        assertFalse(result);
     }
 
-    class SensorEventTimeStampListener implements SensorEventListener {
-        SensorEventTimeStampListener(long eventReportLatencyNs, CountDownLatch latch) {
-            mEventReportLatencyNs = eventReportLatencyNs;
-            mPrevTimeStampNs = -1;
-            mLatch = latch;
-            numErrors = 0;
-        }
-
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            if (mPrevTimeStampNs == -1) {
-                mPrevTimeStampNs = event.timestamp;
-                return;
-            }
-            long currTimeStampNs = event.timestamp;
-            if (currTimeStampNs <= mPrevTimeStampNs) {
-                Log.w(TAG, "Timestamps not monotonically increasing curr_ts_ns=" +
-                        event.timestamp + " prev_ts_ns=" + mPrevTimeStampNs);
-                numErrors++;
-                mPrevTimeStampNs = currTimeStampNs;
-                return;
-            }
-            mLatch.countDown();
-
-            final long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
-
-            if (elapsedRealtimeNs <= currTimeStampNs) {
-                Log.w(TAG, "Timestamps into the future curr elapsedRealTimeNs=" + elapsedRealtimeNs
-                         + " current sensor ts_ns=" + currTimeStampNs);
-                ++numErrors;
-            } else if (elapsedRealtimeNs-currTimeStampNs > SYNC_TOLERANCE + mEventReportLatencyNs) {
-                Log.w(TAG, "Timestamp sync error elapsedRealTimeNs=" + elapsedRealtimeNs +
-                        " curr_ts_ns=" + currTimeStampNs +
-                        " diff_ns=" + (elapsedRealtimeNs - currTimeStampNs) +
-                        " SYNC_TOLERANCE_NS=" + SYNC_TOLERANCE +
-                        " eventReportLatencyNs=" + mEventReportLatencyNs);
-                ++numErrors;
-            }
-            mPrevTimeStampNs = currTimeStampNs;
-        }
-
-        public int getNumErrors() {
-            return numErrors;
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-        }
-
-        private int numErrors;
-        private long mEventReportLatencyNs;
-        private long mPrevTimeStampNs;
-        private final CountDownLatch mLatch;
-        private final long SYNC_TOLERANCE = 500000000L; // 500 milli seconds approx.
-    }
-
-    // Register for each sensor and compare the timestamps of SensorEvents that you get with
-    // elapsedRealTimeNano.
+    // TODO: remove when parametized tests are supported and EventTimestampSynchronization
+    //       verification is added to default verifications
     @TimeoutReq(minutes=60)
     public void testSensorTimeStamps() throws Exception {
-        final int numEvents = 2000;
-        try {
-            mWakeLock.acquire();
-            int numErrors = 0;
-            for (Sensor sensor : mSensorList) {
-                // Skip OEM defined sensors and non continuous sensors.
-                if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
-                    continue;
-                }
-
-                for (int iterations = 0; iterations < 2; ++iterations) {
-                    // Test in both batch mode and non-batch mode for every sensor.
-                    long maxBatchReportLatencyNs = 10000000000L; // 10 secs
-                    if (iterations % 2 == 0) maxBatchReportLatencyNs = 0;
-
-                    final long samplingPeriodNs = (long)(TimeUnit.MICROSECONDS.toNanos(
-                            sensor.getMinDelay())/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE);
-                    // If there is a FIFO and a wake-lock is held, events will be reported when
-                    // the batch timeout expires or when the FIFO is full which ever occurs
-                    // earlier.
-                    final long eventReportLatencyNs = Math.min(maxBatchReportLatencyNs,
-                            sensor.getFifoMaxEventCount() * samplingPeriodNs);
-
-                    final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
-                    SensorEventTimeStampListener listener = new SensorEventTimeStampListener(
-                            eventReportLatencyNs, eventsRemaining);
-
-                    Log.i(TAG, "Running timeStamp test on " + sensor.getName());
-                    boolean result = mSensorManager.registerListener(listener, sensor,
-                            SensorManager.SENSOR_DELAY_FASTEST,
-                            (int)maxBatchReportLatencyNs/1000);
-                    assertTrue("Sensor registerListener failed ", result);
-
-                    long timeToWaitUs = samplingPeriodNs/1000 + eventReportLatencyNs/1000 +
-                            TIMEOUT_TOLERANCE_US;
-                    long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
-                            (int)(eventReportLatencyNs/1000), eventsRemaining);
-
-                    mSensorManager.unregisterListener(listener);
-                    if (eventsRemaining.getCount() > 0) {
-                        failTimedOut(sensor.getName(), (double) totalTimeWaitedUs/1000,
-                                numEvents, (double) sensor.getMinDelay()/1000,
-                                eventsRemaining.getCount(),
-                                numEvents - eventsRemaining.getCount());
-                    }
-                    if (listener.getNumErrors() > 5) {
-                        fail("Check logcat. Timestamp test failed. numErrors=" +
-                                listener.getNumErrors() + " " + sensor.getName() +
-                                " maxBatchReportLatencyNs=" + maxBatchReportLatencyNs +
-                                " samplingPeriodNs=" + sensor.getMinDelay());
-                        numErrors += listener.getNumErrors();
-                    } else {
-                        Log.i(TAG, "TimeStamp test PASS'd on " + sensor.getName());
-                    }
-                }
-            }
-        } finally {
-            mWakeLock.release();
+        ArrayList<Throwable> errorsFound = new ArrayList<>();
+        for (Sensor sensor : mSensorList) {
+            // test both continuous and batching mode sensors
+            verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, errorsFound);
+            verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), errorsFound);
         }
+        assertOnErrors(errorsFound);
     }
 
-    private void failTimedOut(String sensorName, double totalTimeWaitedMs, int numEvents,
-            double minDelayMs, long eventsRemaining, long eventsReceived) {
-        final String TIMED_OUT_FORMAT = "Timed out waiting for events from %s " +
-                "waited for time=%.1fms to receive totalEvents=%d at samplingRate=%.1fHz" +
-                " remainingEvents=%d  received events=%d";
-        fail(String.format(TIMED_OUT_FORMAT, sensorName, totalTimeWaitedMs, numEvents,
-                1000/minDelayMs, eventsRemaining, eventsReceived));
-    }
-
-    // Register for updates from each continuous mode sensor, wait for N events, call flush and
-    // wait for flushCompleteEvent before unregistering for the sensor.
+    // TODO: remove when parameterized tests are supported (see SensorBatchingTests.java)
     @TimeoutReq(minutes=20)
     public void testBatchAndFlush() throws Exception {
-        try {
-            mWakeLock.acquire();
-            for (Sensor sensor : mSensorList) {
-                // Skip ONLY one-shot sensors.
-                if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
-                    registerListenerCallFlush(sensor, null);
-                }
-            }
-        } finally {
-            mWakeLock.release();
+        ArrayList<Throwable> errorsFound = new ArrayList<>();
+        for (Sensor sensor : mSensorList) {
+            verifyRegisterListenerCallFlush(sensor, null /* handler */, errorsFound);
         }
+        assertOnErrors(errorsFound);
     }
 
-    // Same as testBatchAndFlush but using Handler version of the API to register for sensors.
-    // onSensorChanged is now called on a background thread.
+    /**
+     * Verifies that sensor events arrive in the given message queue (Handler).
+     */
     @TimeoutReq(minutes=10)
     public void testBatchAndFlushWithHandler() throws Exception {
-        try {
-            mWakeLock.acquire();
-            HandlerThread handlerThread = new HandlerThread("sensorThread");
-            handlerThread.start();
-            Handler handler = new Handler(handlerThread.getLooper());
-            for (Sensor sensor : mSensorList) {
-                // Skip ONLY one-shot sensors.
-                if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
-                    registerListenerCallFlush(sensor, handler);
-                }
+        Sensor sensor = null;
+        for (Sensor s : mSensorList) {
+            if (s.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+                sensor = s;
+                break;
             }
-        }  finally {
-            mWakeLock.release();
         }
+        if (sensor == null) {
+            throw new SensorTestStateNotSupportedException(
+                    "There are no Continuous sensors in the device.");
+        }
+
+        TestSensorEnvironment environment = new TestSensorEnvironment(
+                getContext(),
+                sensor,
+                SensorManager.SENSOR_DELAY_FASTEST,
+                (int) TimeUnit.SECONDS.toMicros(5));
+        TestSensorManager sensorManager = new TestSensorManager(environment);
+
+        HandlerThread handlerThread = new HandlerThread("sensorThread");
+        handlerThread.start();
+        Handler handler = new Handler(handlerThread.getLooper());
+        TestSensorEventListener listener = new TestSensorEventListener(environment, handler);
+
+        CountDownLatch eventLatch = sensorManager.registerListener(listener, 1);
+        listener.waitForEvents(eventLatch, 1);
+        CountDownLatch flushLatch = sensorManager.requestFlush();
+        listener.waitForFlushComplete(flushLatch);
+        listener.assertEventsReceivedInHandler();
     }
 
-    private void registerListenerCallFlush(Sensor sensor, Handler handler)
-            throws InterruptedException {
-        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
-            return;
-        }
-        final int numEvents = 500;
-        final int rateUs = 0; // DELAY_FASTEST
-        final int maxBatchReportLatencyUs = 10000000;
-        final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
-        final CountDownLatch flushReceived = new CountDownLatch(1);
-        SensorEventListener2 listener = new SensorEventListener2() {
-            @Override
-            public void onSensorChanged(SensorEvent event) {
-                eventsRemaining.countDown();
-            }
-
-            @Override
-            public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            }
-
-            @Override
-            public void onFlushCompleted(Sensor sensor) {
-                flushReceived.countDown();
-            }
-        };
-        // Consider only continuous mode sensors for testing registerListener.
-        // For on-change sensors, call registerListener() so that the listener is associated
-        // with the sensor so that flush(listener) can be called on it.
-        try {
-            Log.i(TAG, "testBatch " + sensor.getName());
-            boolean result = mSensorManager.registerListener(listener, sensor,
-                    rateUs, maxBatchReportLatencyUs, handler);
-            assertTrue("registerListener failed " + sensor.getName(), result);
-
-            // Wait for 500 events or N seconds before the test times out.
-            if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
-                // Wait for approximately the time required to generate these events + a tolerance
-                // of 10 seconds.
-                long timeToWaitUs =
-                  numEvents*(long)(sensor.getMinDelay()/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE)
-                        + maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
-
-                long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
-                        maxBatchReportLatencyUs, eventsRemaining);
-                if (eventsRemaining.getCount() > 0) {
-                    failTimedOut(sensor.getName(), (double)totalTimeWaitedUs/1000, numEvents,
-                            (double)sensor.getMinDelay()/1000,
-                            eventsRemaining.getCount(), numEvents - eventsRemaining.getCount());
-                }
-            }
-            Log.i(TAG, "testFlush " + sensor.getName());
-            result = mSensorManager.flush(listener);
-            assertTrue("flush failed " + sensor.getName(), result);
-            boolean collectedAllEvents = flushReceived.await(TIMEOUT_TOLERANCE_US,
-                    TimeUnit.MICROSECONDS);
-            if (!collectedAllEvents) {
-                fail("Timed out waiting for flushCompleteEvent from " + sensor.getName() +
-                        " waitedFor="+ TIMEOUT_TOLERANCE_US/1000 + "ms");
-            }
-            Log.i(TAG, "testBatchAndFlush PASS " + sensor.getName());
-        } finally {
-            mSensorManager.unregisterListener(listener);
-        }
-    }
-
-    // Wait till the CountDownLatch counts down to zero. If the events are not delivered within
-    // timetoWaitUs, wait an additional maxReportLantencyUs and check if the sensor is streaming
-    // data or not. If the sensor is not streaming at all, fail the test or else wait in increments
-    // of maxReportLantencyUs to collect sensor events.
-    private long waitToCollectAllEvents(long timeToWaitUs, int maxReportLatencyUs,
-            CountDownLatch eventsRemaining)
-            throws InterruptedException {
-        boolean collectedAllEvents = false;
-        long totalTimeWaitedUs = 0;
-        long remainingEvents;
-        final long INCREMENTAL_WAIT_US = maxReportLatencyUs + TimeUnit.SECONDS.toMicros(1);
-        do {
-            totalTimeWaitedUs += timeToWaitUs;
-            remainingEvents = eventsRemaining.getCount();
-            collectedAllEvents = eventsRemaining.await(timeToWaitUs, TimeUnit.MICROSECONDS);
-            timeToWaitUs = INCREMENTAL_WAIT_US;
-        } while (!collectedAllEvents &&
-                (remainingEvents - eventsRemaining.getCount() >=(long)INCREMENTAL_WAIT_US/1000000));
-        return totalTimeWaitedUs;
-    }
-
-    // Call registerListener for multiple sensors at a time and call flush.
-    public void testBatchAndFlushWithMutipleSensors() throws Exception {
-        final int MAX_SENSORS = 3;
+    // TODO: after L release move to SensorBatchingTests and run in all sensors with default
+    //       verifications enabled
+    public void testBatchAndFlushWithMultipleSensors() throws Exception {
+        final int maxSensors = 3;
+        final int maxReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(10);
         List<Sensor> sensorsToTest = new ArrayList<Sensor>();
         for (Sensor sensor : mSensorList) {
             if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
                 sensorsToTest.add(sensor);
-                if (sensorsToTest.size()  == MAX_SENSORS) break;
+                if (sensorsToTest.size()  == maxSensors) break;
             }
         }
-
         final int numSensorsToTest = sensorsToTest.size();
         if (numSensorsToTest == 0) {
             return;
         }
-        final int numEvents = 500;
-        int rateUs = 0; // DELAY_FASTEST
-        final int maxBatchReportLatencyUs = 10000000;
-        final CountDownLatch eventsRemaining = new CountDownLatch(numSensorsToTest * numEvents);
-        final CountDownLatch flushReceived = new CountDownLatch(numSensorsToTest);
-        SensorEventListener2 listener = new SensorEventListener2() {
-            @Override
-            public void onSensorChanged(SensorEvent event) {
-                eventsRemaining.countDown();
-            }
 
-            @Override
-            public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            }
-
-            @Override
-            public void onFlushCompleted(Sensor sensor) {
-                flushReceived.countDown();
-            }
-        };
-
-        try {
-            mWakeLock.acquire();
-            StringBuilder registeredSensors = new StringBuilder(30);
-            for (Sensor sensor : sensorsToTest) {
-                rateUs = Math.max(sensor.getMinDelay(), rateUs);
-                boolean result = mSensorManager.registerListener(listener, sensor,
-                        SensorManager.SENSOR_DELAY_FASTEST, maxBatchReportLatencyUs);
-                assertTrue("registerListener failed for " + sensor.getName(), result);
-                registeredSensors.append(sensor.getName());
-                registeredSensors.append(" ");
-            }
-
-            Log.i(TAG, "testBatchAndFlushWithMutipleSensors " + registeredSensors);
-            long timeToWaitUs =
-                numEvents*(long)(rateUs/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE) +
-                maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
-
-            long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs, maxBatchReportLatencyUs,
-                    eventsRemaining);
-            if (eventsRemaining.getCount() > 0) {
-                failTimedOut(registeredSensors.toString(), (double)totalTimeWaitedUs/1000,
-                        numEvents, (double)rateUs/1000, eventsRemaining.getCount(),
-                        numEvents - eventsRemaining.getCount());
-            }
-            boolean result = mSensorManager.flush(listener);
-            assertTrue("flush failed " + registeredSensors.toString(), result);
-            boolean collectedFlushEvent =
-                flushReceived.await(TIMEOUT_TOLERANCE_US, TimeUnit.MICROSECONDS);
-            if (!collectedFlushEvent) {
-                fail("Timed out waiting for flushCompleteEvent from " +
-                        registeredSensors.toString() + " waited for=" + timeToWaitUs/1000 + "ms");
-            }
-            Log.i(TAG, "testBatchAndFlushWithMutipleSensors PASS'd");
-        } finally {
-            mSensorManager.unregisterListener(listener);
-            mWakeLock.release();
+        StringBuilder builder = new StringBuilder();
+        ParallelSensorOperation parallelSensorOperation = new ParallelSensorOperation();
+        for (Sensor sensor : sensorsToTest) {
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    getContext(),
+                    sensor,
+                    shouldEmulateSensorUnderLoad(),
+                    SensorManager.SENSOR_DELAY_FASTEST,
+                    maxReportLatencyUs);
+            FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+            parallelSensorOperation.add(new TestSensorOperation(environment, executor));
+            builder.append(sensor.getName()).append(", ");
         }
+
+        Log.i(TAG, "Testing batch/flush for sensors: " + builder);
+        parallelSensorOperation.execute(getCurrentTestNode());
     }
 
     private void assertSensorValues(Sensor sensor) {
@@ -583,8 +380,8 @@
         assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
                 " " + sensor.getName(), sensor.getResolution() >= 0);
         assertNotNull("Vendor name must not be null " + sensor.getName(), sensor.getVendor());
-        assertTrue("Version must be positive version="  + sensor.getVersion() + " " +
-                    sensor.getName(), sensor.getVersion() > 0);
+        assertTrue("Version must be positive version=" + sensor.getVersion() + " " +
+                sensor.getName(), sensor.getVersion() > 0);
         int fifoMaxEventCount = sensor.getFifoMaxEventCount();
         int fifoReservedEventCount = sensor.getFifoReservedEventCount();
         assertTrue(fifoMaxEventCount >= 0);
@@ -617,19 +414,142 @@
         assertEquals(sensors, mSensorManager.getSensors());
     }
 
-    class TriggerListener extends TriggerEventListener {
-        @Override
-        public void onTrigger(TriggerEvent event) {
+    /**
+     * Verifies that a continuous sensor produces events that have timestamps synchronized with
+     * {@link SystemClock#elapsedRealtimeNanos()}.
+     */
+    private void verifyLongActivation(
+            Sensor sensor,
+            int maxReportLatencyUs,
+            ArrayList<Throwable> errorsFound) throws InterruptedException {
+        if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
+            return;
+        }
+
+        try {
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    getContext(),
+                    sensor,
+                    shouldEmulateSensorUnderLoad(),
+                    SensorManager.SENSOR_DELAY_FASTEST,
+                    maxReportLatencyUs);
+            TestSensorOperation operation =
+                    TestSensorOperation.createOperation(environment, 20, TimeUnit.SECONDS);
+            operation.addVerification(EventGapVerification.getDefault(environment));
+            operation.addVerification(EventOrderingVerification.getDefault(environment));
+            operation.addVerification(
+                    EventTimestampSynchronizationVerification.getDefault(environment));
+
+            Log.i(TAG, "Running timestamp test on: " + sensor.getName());
+            operation.execute(getCurrentTestNode());
+        } catch (InterruptedException e) {
+            // propagate so the test can stop
+            throw e;
+        } catch (Throwable e) {
+            errorsFound.add(e);
+            Log.e(TAG, e.getMessage());
         }
     }
 
-    class SensorListener implements SensorEventListener {
-        @Override
-        public void onSensorChanged(SensorEvent event) {
+    /**
+     * Verifies that a client can listen for events, and that
+     * {@link SensorManager#flush(SensorEventListener)} will trigger the appropriate notification
+     * for {@link SensorEventListener2#onFlushCompleted(Sensor)}.
+     */
+    private void verifyRegisterListenerCallFlush(
+            Sensor sensor,
+            Handler handler,
+            ArrayList<Throwable> errorsFound)
+            throws InterruptedException {
+        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+            return;
         }
 
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        try {
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    getContext(),
+                    sensor,
+                    shouldEmulateSensorUnderLoad(),
+                    SensorManager.SENSOR_DELAY_FASTEST,
+                    (int) TimeUnit.SECONDS.toMicros(10));
+            FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+            TestSensorOperation operation = new TestSensorOperation(environment, executor, handler);
+
+            Log.i(TAG, "Running flush test on: " + sensor.getName());
+            operation.execute(getCurrentTestNode());
+        } catch (InterruptedException e) {
+            // propagate so the test can stop
+            throw e;
+        } catch (Throwable e) {
+            errorsFound.add(e);
+            Log.e(TAG, e.getMessage());
         }
     }
+
+    private void assertOnErrors(List<Throwable> errorsFound) {
+        if (!errorsFound.isEmpty()) {
+            StringBuilder builder = new StringBuilder();
+            for (Throwable error : errorsFound) {
+                builder.append(error.getMessage()).append("\n");
+            }
+            Assert.fail(builder.toString());
+        }
+    }
+
+    /**
+     * A delegate that drives the execution of Batch/Flush tests.
+     * It performs several operations in order:
+     * - registration
+     * - for continuous sensors it first ensures that the FIFO is filled
+     *      - if events do not arrive on time, an assert will be triggered
+     * - requests flush of sensor data
+     * - waits for {@link SensorEventListener2#onFlushCompleted(Sensor)}
+     *      - if the event does not arrive, an assert will be triggered
+     */
+    private class FlushExecutor implements TestSensorOperation.Executor {
+        private final TestSensorEnvironment mEnvironment;
+        private final int mEventCount;
+
+        public FlushExecutor(TestSensorEnvironment environment, int eventCount) {
+            mEnvironment = environment;
+            mEventCount = eventCount;
+        }
+
+        /**
+         * Consider only continuous mode sensors for testing register listener.
+         *
+         * For on-change sensors, we only use
+         * {@link TestSensorManager#registerListener(TestSensorEventListener)} to associate the
+         * listener with the sensor. So that {@link TestSensorManager#requestFlush()} can be
+         * invoked on it.
+         */
+        @Override
+        public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                throws InterruptedException {
+            int sensorReportingMode = mEnvironment.getSensor().getReportingMode();
+            try {
+                CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount);
+                if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
+                    listener.waitForEvents(eventLatch, mEventCount);
+                }
+                CountDownLatch flushLatch = sensorManager.requestFlush();
+                listener.waitForFlushComplete(flushLatch);
+            } finally {
+                sensorManager.unregisterListener();
+            }
+        }
+    }
+
+    private class NullTriggerEventListener extends TriggerEventListener {
+        @Override
+        public void onTrigger(TriggerEvent event) {}
+    }
+
+    private class NullSensorEventListener implements SensorEventListener {
+        @Override
+        public void onSensorChanged(SensorEvent event) {}
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
index 6454678..42b8d33 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -16,17 +16,11 @@
 
 package android.hardware.cts;
 
-import com.android.cts.util.ReportLog;
-import com.android.cts.util.ResultType;
-import com.android.cts.util.ResultUnit;
-
-import android.app.Instrumentation;
-import android.cts.util.DeviceReportLog;
 import android.hardware.Sensor;
-import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
 import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -35,25 +29,30 @@
  */
 public abstract class SensorTestCase extends AndroidTestCase {
     // TODO: consolidate all log tags
-    protected final String LOG_TAG = "TestRunner";
+    protected static final String LOG_TAG = "TestRunner";
 
     /**
      * By default tests need to run in a {@link TestSensorEnvironment} that assumes each sensor is
      * running with a load of several listeners, requesting data at different rates.
      *
-     * In a better world the component acting as builder of {@link ISensorOperation} would compute
+     * In a better world the component acting as builder of {@link SensorOperation} would compute
      * this value based on the tests composed.
      *
      * Ideally, each {@link Sensor} object would expose this information to clients.
      */
     private volatile boolean mEmulateSensorUnderLoad = true;
 
+    /**
+     * By default the test class is the root of the test hierarchy.
+     */
+    private volatile ISensorTestNode mCurrentTestNode = new TestClassNode(getClass());
+
     protected SensorTestCase() {}
 
     @Override
-    public void runTest() throws Throwable {
+    public void runBare() throws Throwable {
         try {
-            super.runTest();
+            super.runBare();
         } catch (SensorTestStateNotSupportedException e) {
             // the sensor state is not supported in the device, log a warning and skip the test
             Log.w(LOG_TAG, e.getMessage());
@@ -68,33 +67,24 @@
         return mEmulateSensorUnderLoad;
     }
 
-    /**
-     * Utility method to log selected stats to a {@link ReportLog} object.  The stats must be
-     * a number or an array of numbers.
-     */
-    public static void logSelectedStatsToReportLog(Instrumentation instrumentation, int depth,
-            String[] keys, SensorStats stats) {
-        DeviceReportLog reportLog = new DeviceReportLog(depth);
+    public void setCurrentTestNode(ISensorTestNode value) {
+        mCurrentTestNode = value;
+    }
 
-        for (String key : keys) {
-            Object value = stats.getValue(key);
-            if (value instanceof Integer) {
-                reportLog.printValue(key, (Integer) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof Double) {
-                reportLog.printValue(key, (Double) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof Float) {
-                reportLog.printValue(key, (Float) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof double[]) {
-                reportLog.printArray(key, (double[]) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof float[]) {
-                float[] tmpFloat = (float[]) value;
-                double[] tmpDouble = new double[tmpFloat.length];
-                for (int i = 0; i < tmpDouble.length; i++) tmpDouble[i] = tmpFloat[i];
-                reportLog.printArray(key, tmpDouble, ResultType.NEUTRAL, ResultUnit.NONE);
-            }
+    protected ISensorTestNode getCurrentTestNode() {
+        return mCurrentTestNode;
+    }
+
+    private class TestClassNode implements ISensorTestNode {
+        private final Class<?> mTestClass;
+
+        public TestClassNode(Class<?> testClass) {
+            mTestClass = testClass;
         }
 
-        reportLog.printSummary("summary", 0, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.deliverReportToHost(instrumentation);
+        @Override
+        public String getName() {
+            return mTestClass.getSimpleName();
+        }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index 3bd4a03..0fbd8fa 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -22,6 +22,7 @@
 import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.content.pm.PackageManager;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -105,9 +106,14 @@
      */
     public void testSensorProperties() {
         // sensor type: [getMinDelay()]
-        Map<Integer, Object[]> expectedProperties = new HashMap<Integer, Object[]>(3);
-        expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{10000});
-        expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{10000});
+        Map<Integer, Object[]> expectedProperties = new HashMap<>(3);
+        if(getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+                expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{20000});
+                expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{20000});
+        }else {
+                expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{10000});
+                expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{10000});
+        }
         expectedProperties.put(Sensor.TYPE_MAGNETIC_FIELD, new Object[]{100000});
 
         SensorManager sensorManager =
@@ -541,25 +547,21 @@
                 sensorType,
                 shouldEmulateSensorUnderLoad(),
                 rateUs);
-        TestSensorOperation op = new TestSensorOperation(environment, 5, TimeUnit.SECONDS);
+        TestSensorOperation op =
+                TestSensorOperation.createOperation(environment, 5, TimeUnit.SECONDS);
         op.addDefaultVerifications();
-        op.setLogEvents(true);
-        try {
-            op.execute();
-        } finally {
-            SensorStats.logStats(TAG, op.getStats());
 
-            String sensorRate;
-            if (rateUs == SensorManager.SENSOR_DELAY_FASTEST) {
-                sensorRate = "fastest";
-            } else {
-                sensorRate = String.format("%.0fhz", environment.getFrequencyHz());
-            }
+        try {
+            op.execute(getCurrentTestNode());
+        } finally {
+            SensorStats stats = op.getStats();
+            stats.log(TAG);
+
             String fileName = String.format(
                     "single_%s_%s.txt",
                     SensorStats.getSanitizedSensorName(environment.getSensor()),
-                    sensorRate);
-            SensorStats.logStatsToFile(fileName, op.getStats());
+                    environment.getFrequencyString());
+            stats.logToFile(fileName);
         }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
deleted file mode 100644
index ca7d133..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
+++ /dev/null
@@ -1,101 +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.hardware.cts.helpers;
-
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A {@link TestSensorEventListener} which collects events to be processed after the test is run.
- * This should only be used for short tests.
- */
-public class CollectingSensorEventListener extends TestSensorEventListener {
-    private final ArrayList<TestSensorEvent> mSensorEventsList = new ArrayList<TestSensorEvent>();
-
-    /**
-     * Constructs a {@link CollectingSensorEventListener} with an additional
-     * {@link SensorEventListener2}.
-     */
-    public CollectingSensorEventListener(SensorEventListener2 listener) {
-        super(listener);
-    }
-
-    /**
-     * Constructs a {@link CollectingSensorEventListener}.
-     */
-    public CollectingSensorEventListener() {
-        this(null);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        super.onSensorChanged(event);
-        synchronized (mSensorEventsList) {
-            mSensorEventsList.add(new TestSensorEvent(event));
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Clears the event queue before starting.
-     * </p>
-     */
-    @Override
-    public void waitForEvents(int eventCount) throws InterruptedException {
-        clearEvents();
-        super.waitForEvents(eventCount);
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Clears the event queue before starting.
-     * </p>
-     */
-    @Override
-    public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
-        clearEvents();
-        super.waitForEvents(duration, timeUnit);
-    }
-
-    /**
-     * Get the {@link TestSensorEvent} array from the event queue.
-     */
-    public List<TestSensorEvent> getEvents() {
-        synchronized (mSensorEventsList) {
-            return Collections.unmodifiableList(mSensorEventsList);
-        }
-    }
-
-    /**
-     * Clear the event queue.
-     */
-    public void clearEvents() {
-        synchronized (mSensorEventsList) {
-            mSensorEventsList.clear();
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
index b3b8559..d3dcf37 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
@@ -35,6 +35,8 @@
 
     private final TestSensorManager mCalibratedSensorManager;
     private final TestSensorManager mUncalibratedSensorManager;
+    private final TestSensorEventListener mCalibratedTestListener;
+    private final TestSensorEventListener mUncalibratedTestListener;
     private final float mThreshold;
 
     public SensorCalibratedUncalibratedVerifier(
@@ -43,6 +45,8 @@
             float threshold) {
         mCalibratedSensorManager = new TestSensorManager(calibratedEnvironment);
         mUncalibratedSensorManager = new TestSensorManager(uncalibratedEnvironment);
+        mCalibratedTestListener = new TestSensorEventListener(calibratedEnvironment);
+        mUncalibratedTestListener = new TestSensorEventListener(uncalibratedEnvironment);
         mThreshold = threshold;
     }
 
@@ -50,10 +54,8 @@
      * Executes the operation: it collects the data and run verifications on it.
      */
     public void execute() throws Throwable {
-        CollectingSensorEventListener calibratedTestListener = new CollectingSensorEventListener();
-        CollectingSensorEventListener uncalibratedTestListener = new CollectingSensorEventListener();
-        mCalibratedSensorManager.registerListener(calibratedTestListener);
-        mUncalibratedSensorManager.registerListener(uncalibratedTestListener);
+        mCalibratedSensorManager.registerListener(mCalibratedTestListener);
+        mUncalibratedSensorManager.registerListener(mUncalibratedTestListener);
 
         Thread.sleep(TimeUnit.SECONDS.toMillis(10));
 
@@ -61,8 +63,8 @@
         mUncalibratedSensorManager.unregisterListener();
 
         verifyMeasurements(
-                calibratedTestListener.getEvents(),
-                uncalibratedTestListener.getEvents(),
+                mCalibratedTestListener.getCollectedEvents(),
+                mUncalibratedTestListener.getCollectedEvents(),
                 mThreshold);
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index a79e5b1..490e965 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -192,7 +192,7 @@
             TestSensorEnvironment environment,
             String extras) {
         return String.format(
-                "%s | sensor='%s', samplingPeriodUs=%d, maxReportLatencyUs=%d | %s",
+                "%s | sensor='%s', samplingPeriod=%dus, maxReportLatency=%dus | %s",
                 label,
                 environment.getSensor().getName(),
                 environment.getRequestedSamplingPeriodUs(),
@@ -219,6 +219,25 @@
     }
 
     /**
+     * Sanitizes a string so it can be used in file names.
+     *
+     * @param value The string to sanitize.
+     * @return The sanitized string.
+     *
+     * @throws SensorTestPlatformException If the string cannot be sanitized.
+     */
+    public static String sanitizeStringForFileName(String value)
+            throws SensorTestPlatformException {
+        String sanitizedValue = value.replaceAll("[^a-zA-Z0-9_\\-]", "_");
+        if (sanitizedValue.matches("_*")) {
+            throw new SensorTestPlatformException(
+                    "Unable to sanitize string '%s' for file name.",
+                    value);
+        }
+        return sanitizedValue;
+    }
+
+    /**
      * Ensures that the directory structure represented by the given {@link File} is created.
      */
     private static File createDirectoryStructure(File directoryStructure) throws IOException {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 7be6c0c..8067aed 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -17,7 +17,7 @@
 package android.hardware.cts.helpers;
 
 import android.hardware.Sensor;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
 import android.util.Log;
 
 import java.io.BufferedWriter;
@@ -34,28 +34,29 @@
 import java.util.Set;
 
 /**
- * Class used to store stats related to {@link ISensorOperation}s.  Sensor stats may be linked
+ * Class used to store stats related to {@link SensorOperation}s. Sensor stats may be linked
  * together so that they form a tree.
  */
 public class SensorStats {
     public static final String DELIMITER = "__";
 
-    public static final String FIRST_TIMESTAMP_KEY = "first_timestamp";
-    public static final String LAST_TIMESTAMP_KEY = "last_timestamp";
     public static final String ERROR = "error";
-    public static final String EVENT_COUNT_KEY = "event_count";
     public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
     public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
     public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
     public static final String EVENT_OUT_OF_ORDER_POSITIONS_KEY = "event_out_of_order_positions";
+    public static final String EVENT_TIME_SYNCHRONIZATION_COUNT_KEY =
+            "event_time_synchronization_count";
+    public static final String EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY =
+            "event_time_synchronization_positions";
     public static final String FREQUENCY_KEY = "frequency";
     public static final String JITTER_95_PERCENTILE_PERCENT_KEY = "jitter_95_percentile_percent";
     public static final String MEAN_KEY = "mean";
     public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
     public static final String MAGNITUDE_KEY = "magnitude";
 
-    private final Map<String, Object> mValues = new HashMap<String, Object>();
-    private final Map<String, SensorStats> mSensorStats = new HashMap<String, SensorStats>();
+    private final Map<String, Object> mValues = new HashMap<>();
+    private final Map<String, SensorStats> mSensorStats = new HashMap<>();
 
     /**
      * Add a value.
@@ -72,7 +73,7 @@
 
     /**
      * Add a nested {@link SensorStats}. This is useful for keeping track of stats in a
-     * {@link ISensorOperation} tree.
+     * {@link SensorOperation} tree.
      *
      * @param key the key
      * @param stats the sub {@link SensorStats} object.
@@ -103,13 +104,13 @@
     /**
      * Flattens the map and all sub {@link SensorStats} objects. Keys will be flattened using
      * {@value #DELIMITER}. For example, if a sub {@link SensorStats} is added with key
-     * {@code "key1"} containing the key value pair {@code ("key2", "value")}, the flattened map
-     * will contain the entry {@code ("key1__key2", "value")}.
+     * {@code "key1"} containing the key value pair {@code \("key2", "value"\)}, the flattened map
+     * will contain the entry {@code \("key1__key2", "value"\)}.
      *
      * @return a {@link Map} containing all stats from the value and sub {@link SensorStats}.
      */
     public synchronized Map<String, Object> flatten() {
-        final Map<String, Object> flattenedMap = new HashMap<String, Object>(mValues);
+        final Map<String, Object> flattenedMap = new HashMap<>(mValues);
         for (Entry<String, SensorStats> statsEntry : mSensorStats.entrySet()) {
             for (Entry<String, Object> valueEntry : statsEntry.getValue().flatten().entrySet()) {
                 String key = statsEntry.getKey() + DELIMITER + valueEntry.getKey();
@@ -122,8 +123,8 @@
     /**
      * Utility method to log the stats to the logcat.
      */
-    public static void logStats(String tag, SensorStats stats) {
-        final Map<String, Object> flattened = stats.flatten();
+    public void log(String tag) {
+        final Map<String, Object> flattened = flatten();
         for (String key : getSortedKeys(flattened)) {
             Object value = flattened.get(key);
             Log.v(tag, String.format("%s: %s", key, getValueString(value)));
@@ -133,39 +134,29 @@
     /**
      * Utility method to log the stats to a file. Will overwrite the file if it already exists.
      */
-    public static void logStatsToFile(String fileName, SensorStats stats) throws IOException {
+    public void logToFile(String fileName) throws IOException {
         File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
         File logFile = new File(statsDirectory, fileName);
-        final BufferedWriter writer =
-                new BufferedWriter(new FileWriter(logFile, false /* append */));
-        final Map<String, Object> flattened = stats.flatten();
-        try {
+        final Map<String, Object> flattened = flatten();
+        FileWriter fileWriter = new FileWriter(logFile, false /* append */);
+        try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
             for (String key : getSortedKeys(flattened)) {
                 Object value = flattened.get(key);
                 writer.write(String.format("%s: %s\n", key, getValueString(value)));
             }
-        } finally {
-            writer.flush();
-            writer.close();
         }
     }
 
     /**
      * Provides a sanitized sensor name, that can be used in file names.
-     * See {@link #logStatsToFile(String, SensorStats)}.
+     * See {@link #logToFile(String)}.
      */
-    public static String getSanitizedSensorName(Sensor sensor) throws IOException {
-        String sensorType = sensor.getStringType();
-        String sanitizedSensorType = sensorType.replaceAll("[^a-zA-Z0-9_\\-]", "_");
-        if (sanitizedSensorType.matches("_*")) {
-            throw new IOException("Unable to sanitize sensor type (" + sensorType + "). This is a"
-                    + " 'test framework' issue and the sanitation routine must be fixed.");
-        }
-        return sanitizedSensorType;
+    public static String getSanitizedSensorName(Sensor sensor) throws SensorTestPlatformException {
+        return SensorCtsHelper.sanitizeStringForFileName(sensor.getStringType());
     }
 
     private static List<String> getSortedKeys(Map<String, Object> flattenedStats) {
-        List<String> keys = new ArrayList<String>(flattenedStats.keySet());
+        List<String> keys = new ArrayList<>(flattenedStats.keySet());
         Collections.sort(keys);
         return keys;
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestPlatformException.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestPlatformException.java
new file mode 100644
index 0000000..b230749
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestPlatformException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.hardware.cts.helpers;
+
+/**
+ * Exception that indicates an issue in the Sensor Test Platform.
+ * It usually constitutes a bug in the platform that needs to be fixed.
+ */
+public class SensorTestPlatformException extends Exception {
+    public SensorTestPlatformException(String format, Object ... params) {
+        this(String.format(format, params));
+    }
+
+    public SensorTestPlatformException(String message) {
+        super(message + " (This is a 'sensor test platform' issue, please report it so it can be fixed)");
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 8f33f92..7d10f91 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -19,15 +19,22 @@
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
 
 import java.util.concurrent.TimeUnit;
 
 /**
- * A class that encapsulates base environment information for the {@link ISensorOperation}.
+ * A class that encapsulates base environment information for the {@link SensorOperation}.
  * The environment is self contained and carries its state around all the sensor test framework.
  */
 public class TestSensorEnvironment {
+
+    /**
+     * It represents the fraction of the expected sampling frequency, at which the sensor can
+     * actually produce events.
+     */
+    private static final float MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER = 0.9f;
+
     private final Context mContext;
     private final Sensor mSensor;
     private final boolean mSensorMightHaveMoreListeners;
@@ -40,7 +47,10 @@
      * @param context The context for the test
      * @param sensorType The type of the sensor under test
      * @param samplingPeriodUs The requested collection period for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(Context context, int sensorType, int samplingPeriodUs) {
         this(context, sensorType, false /* sensorMightHaveMoreListeners */, samplingPeriodUs);
     }
@@ -52,7 +62,10 @@
      * @param sensorType The type of the sensor under test
      * @param samplingPeriodUs The requested collection period for the sensor under test
      * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(
             Context context,
             int sensorType,
@@ -72,7 +85,10 @@
      * @param sensorType The type of the sensor under test
      * @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
      * @param samplingPeriodUs The requested collection period for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(
             Context context,
             int sensorType,
@@ -93,7 +109,10 @@
      * @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
      * @param samplingPeriodUs The requested collection period for the sensor under test
      * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(
             Context context,
             int sensorType,
@@ -112,6 +131,26 @@
      *
      * @param context The context for the test
      * @param sensor The sensor under test
+     * @param samplingPeriodUs The requested collection period for the sensor under test
+     * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+     */
+    public TestSensorEnvironment(
+            Context context,
+            Sensor sensor,
+            int samplingPeriodUs,
+            int maxReportLatencyUs) {
+        this(context,
+                sensor,
+                false /* sensorMightHaveMoreListeners */,
+                samplingPeriodUs,
+                maxReportLatencyUs);
+    }
+
+    /**
+     * Constructs an environment for sensor testing.
+     *
+     * @param context The context for the test
+     * @param sensor The sensor under test
      * @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load (this
      *                                     usually implies that there are several listeners
      *                                     requesting different sampling periods)
@@ -160,6 +199,17 @@
     }
 
     /**
+     * @return A string representing the frequency equivalent to
+     * {@link #getRequestedSamplingPeriodUs()}.
+     */
+    public String getFrequencyString() {
+        if (mSamplingPeriodUs == SensorManager.SENSOR_DELAY_FASTEST) {
+            return "fastest";
+        }
+        return String.format("%.0fhz", getFrequencyHz());
+    }
+
+    /**
      * @return The requested collection max batch report latency in microseconds.
      */
     public int getMaxReportLatencyUs() {
@@ -171,7 +221,8 @@
      * data at different sampling rates (the rates are unknown); false otherwise.
      */
     public boolean isSensorSamplingRateOverloaded() {
-        return mSensorMightHaveMoreListeners && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
+        return mSensorMightHaveMoreListeners
+                && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
     }
 
     /**
@@ -197,6 +248,15 @@
     }
 
     /**
+     * @return The actual sampling period at which a sensor can sample data. This value is a
+     *         fraction of {@link #getExpectedSamplingPeriodUs()}.
+     */
+    public int getMaximumExpectedSamplingPeriodUs() {
+        int expectedSamplingPeriodUs = getExpectedSamplingPeriodUs();
+        return (int) (expectedSamplingPeriodUs / MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER);
+    }
+
+    /**
      * @return The number of axes in the coordinate system of the sensor under test.
      */
     public int getSensorAxesCount() {
@@ -266,6 +326,17 @@
         return TimeUnit.SECONDS.toNanos(reportLatencySec);
     }
 
+    @Override
+    public String toString() {
+        return String.format(
+                "Sensor='%s', SamplingRateOverloaded=%s, SamplingPeriod=%sus, "
+                        + "MaxReportLatency=%sus",
+                mSensor,
+                isSensorSamplingRateOverloaded(),
+                mSamplingPeriodUs,
+                mMaxReportLatencyUs);
+    }
+
     /**
      * Return true if {@link #getRequestedSamplingPeriodUs()} is not one of
      * {@link SensorManager#SENSOR_DELAY_GAME}, {@link SensorManager#SENSOR_DELAY_UI}, or
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
index e8500f1..86b2436 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
@@ -21,6 +21,8 @@
 import android.hardware.SensorEventListener2;
 import android.os.SystemClock;
 
+import java.util.Arrays;
+
 /**
  * Class for holding information about individual {@link SensorEvent}s.
  */
@@ -75,4 +77,14 @@
         this.accuracy = accuracy;
         this.values = values;
     }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "Timestamp=%sns, ReceivedTimestamp=%sns, Accuracy=%s, Values=%s",
+                this.timestamp,
+                this.receivedTimestamp,
+                this.accuracy,
+                Arrays.toString(this.values));
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 6567be2..7b1a499 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -21,12 +21,21 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener2;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.SystemClock;
-import android.util.Log;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * A {@link SensorEventListener2} which performs operations such as waiting for a specific number of
@@ -36,51 +45,39 @@
  */
 public class TestSensorEventListener implements SensorEventListener2 {
     public static final String LOG_TAG = "TestSensorEventListener";
-    private static final long EVENT_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
-    private static final long FLUSH_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
 
-    private final SensorEventListener2 mListener;
+    private static final long EVENT_TIMEOUT_US = TimeUnit.SECONDS.toMicros(5);
+    private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
 
-    private volatile CountDownLatch mEventLatch;
-    private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
-    private volatile TestSensorEnvironment mEnvironment;
-    private volatile boolean mLogEvents;
+    private final List<TestSensorEvent> mCollectedEvents = new ArrayList<>();
+    private final List<CountDownLatch> mEventLatches = new ArrayList<>();
+    private final List<CountDownLatch> mFlushLatches = new ArrayList<>();
+    private final AtomicInteger mEventsReceivedOutsideHandler = new AtomicInteger();
+
+    private final Handler mHandler;
+    private final TestSensorEnvironment mEnvironment;
+
+    /**
+     * @deprecated Use {@link TestSensorEventListener(TestSensorEnvironment)}.
+     */
+    @Deprecated
+    public TestSensorEventListener() {
+        this(null /* environment */);
+    }
 
     /**
      * Construct a {@link TestSensorEventListener}.
      */
-    public TestSensorEventListener() {
-        this(null);
+    public TestSensorEventListener(TestSensorEnvironment environment) {
+        this(environment, null /* handler */);
     }
 
     /**
-     * Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}.
+     * Construct a {@link TestSensorEventListener}.
      */
-    public TestSensorEventListener(SensorEventListener2 listener) {
-        if (listener != null) {
-            mListener = listener;
-        } else {
-            // use a Null Object to simplify handling the listener
-            mListener = new SensorEventListener2() {
-                public void onFlushCompleted(Sensor sensor) {}
-                public void onSensorChanged(SensorEvent sensorEvent) {}
-                public void onAccuracyChanged(Sensor sensor, int i) {}
-            };
-        }
-    }
-
-    /**
-     * Set the sensor, rate, and batch report latency used for the assertions.
-     */
-    public void setEnvironment(TestSensorEnvironment environment) {
+    public TestSensorEventListener(TestSensorEnvironment environment, Handler handler) {
         mEnvironment = environment;
-    }
-
-    /**
-     * Set whether or not to log events
-     */
-    public void setLogEvents(boolean log) {
-        mLogEvents = log;
+        mHandler = handler;
     }
 
     /**
@@ -88,19 +85,15 @@
      */
     @Override
     public void onSensorChanged(SensorEvent event) {
-        mListener.onSensorChanged(event);
-        if (mLogEvents) {
-            Log.v(LOG_TAG, String.format(
-                    "Sensor %d: sensor_timestamp=%dns, received_timestamp=%dns, values=%s",
-                    mEnvironment.getSensor().getType(),
-                    event.timestamp,
-                    SystemClock.elapsedRealtimeNanos(),
-                    Arrays.toString(event.values)));
+        long timestampNs = SystemClock.elapsedRealtimeNanos();
+        checkHandler();
+        synchronized (mCollectedEvents) {
+            mCollectedEvents.add(new TestSensorEvent(event, timestampNs));
         }
-
-        CountDownLatch eventLatch = mEventLatch;
-        if(eventLatch != null) {
-            eventLatch.countDown();
+        synchronized (mEventLatches) {
+            for (CountDownLatch latch : mEventLatches) {
+                latch.countDown();
+            }
         }
     }
 
@@ -109,7 +102,32 @@
      */
     @Override
     public void onAccuracyChanged(Sensor sensor, int accuracy) {
-        mListener.onAccuracyChanged(sensor, accuracy);
+        checkHandler();
+    }
+
+    /**
+     * @param eventCount
+     * @return A CountDownLatch initialzed with eventCount and decremented as sensor events arrive
+     * for this listerner.
+     */
+    public CountDownLatch getLatchForSensorEvents(int eventCount) {
+        CountDownLatch latch = new CountDownLatch(eventCount);
+        synchronized (mEventLatches) {
+            mEventLatches.add(latch);
+        }
+        return latch;
+    }
+
+    /**
+     * @return A CountDownLatch initialzed with 1 and decremented as a flush complete arrives
+     * for this listerner.
+     */
+    public CountDownLatch getLatchForFlushCompleteEvent() {
+        CountDownLatch latch = new CountDownLatch(1);
+        synchronized (mFlushLatches) {
+            mFlushLatches.add(latch);
+        }
+        return latch;
     }
 
     /**
@@ -117,12 +135,71 @@
      */
     @Override
     public void onFlushCompleted(Sensor sensor) {
-        CountDownLatch latch = mFlushLatch;
-        mFlushLatch = null;
-        if(latch != null) {
-            latch.countDown();
+        checkHandler();
+        synchronized (mFlushLatches) {
+            for (CountDownLatch latch : mFlushLatches) {
+                latch.countDown();
+            }
         }
-        mListener.onFlushCompleted(sensor);
+    }
+
+    /**
+     * @return The handler (if any) associated with the instance.
+     */
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    /**
+     * @return A list of {@link TestSensorEvent}s collected by the listener.
+     */
+    public List<TestSensorEvent> getCollectedEvents() {
+        synchronized (mCollectedEvents){
+            return Collections.unmodifiableList(mCollectedEvents);
+        }
+    }
+
+    /**
+     * Clears the internal list of collected {@link TestSensorEvent}s.
+     */
+    public void clearEvents() {
+        synchronized (mCollectedEvents) {
+            mCollectedEvents.clear();
+        }
+    }
+
+
+    /**
+     * Utility method to log the collected events to a file.
+     * It will overwrite the file if it already exists, the file is created in a relative directory
+     * named 'events' under the sensor test directory (part of external storage).
+     */
+    public void logCollectedEventsToFile(String fileName) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Sensor='").append(mEnvironment.getSensor()).append("', ");
+        builder.append("SamplingRateOverloaded=")
+                .append(mEnvironment.isSensorSamplingRateOverloaded()).append(", ");
+        builder.append("RequestedSamplingPeriod=")
+                .append(mEnvironment.getRequestedSamplingPeriodUs()).append("us, ");
+        builder.append("MaxReportLatency=")
+                .append(mEnvironment.getMaxReportLatencyUs()).append("us");
+
+        synchronized (mCollectedEvents) {
+            for (TestSensorEvent event : mCollectedEvents) {
+                builder.append("\n");
+                builder.append("Timestamp=").append(event.timestamp).append("ns, ");
+                builder.append("ReceivedTimestamp=").append(event.receivedTimestamp).append("ns, ");
+                builder.append("Accuracy=").append(event.accuracy).append(", ");
+                builder.append("Values=").append(Arrays.toString(event.values));
+            }
+        }
+
+        File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory("events/");
+        File logFile = new File(eventsDirectory, fileName);
+        FileWriter fileWriter = new FileWriter(logFile, false /* append */);
+        try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
+            writer.write(builder.toString());
+        }
     }
 
     /**
@@ -130,14 +207,20 @@
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForFlushComplete() throws InterruptedException {
-        CountDownLatch latch = mFlushLatch;
-        if(latch == null) {
-            return;
+    public void waitForFlushComplete(CountDownLatch latch) throws InterruptedException {
+        clearEvents();
+        try {
+            String message = SensorCtsHelper.formatAssertionMessage(
+                    "WaitForFlush",
+                    mEnvironment,
+                    "timeout=%dus",
+                    FLUSH_TIMEOUT_US);
+            Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+        } finally {
+            synchronized (mFlushLatches) {
+                mFlushLatches.remove(latch);
+            }
         }
-        Assert.assertTrue(
-                SensorCtsHelper.formatAssertionMessage("WaitForFlush", mEnvironment),
-                latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
     }
 
     /**
@@ -145,23 +228,32 @@
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForEvents(int eventCount) throws InterruptedException {
-        mEventLatch = new CountDownLatch(eventCount);
+    public void waitForEvents(CountDownLatch latch, int eventCount) throws InterruptedException {
+        clearEvents();
         try {
-            int rateUs = mEnvironment.getExpectedSamplingPeriodUs();
-            // Timeout is 2 * event count * expected period + batch timeout + default wait
-            long timeoutUs = (2 * eventCount * rateUs)
+            long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
+            // timeout is 2 * event count * expected period + batch timeout + default wait
+            // we multiply by two as not to raise an error in this function even if the events are
+            // streaming at a lower rate than expected, as long as it's not streaming twice as slow
+            // as expected
+            long timeoutUs = (2 * eventCount * samplingPeriodUs)
                     + mEnvironment.getMaxReportLatencyUs()
                     + EVENT_TIMEOUT_US;
-            String message = SensorCtsHelper.formatAssertionMessage(
-                    "WaitForEvents",
-                    mEnvironment,
-                    "requested:%d, received:%d",
-                    eventCount,
-                    eventCount - mEventLatch.getCount());
-            Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
+            boolean success = latch.await(timeoutUs, TimeUnit.MICROSECONDS);
+            if (!success) {
+                String message = SensorCtsHelper.formatAssertionMessage(
+                        "WaitForEvents",
+                        mEnvironment,
+                        "requested=%d, received=%d, timeout=%dus",
+                        eventCount,
+                        eventCount - latch.getCount(),
+                        timeoutUs);
+                Assert.fail(message);
+            }
         } finally {
-            mEventLatch = null;
+            synchronized (mEventLatches) {
+                mEventLatches.remove(latch);
+            }
         }
     }
 
@@ -171,4 +263,28 @@
     public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
         SensorCtsHelper.sleep(duration, timeUnit);
     }
+
+    /**
+     * Asserts that sensor events arrived in the proper thread if a {@link Handler} was associated
+     * with the current instance.
+     *
+     * If no events were received this assertion will be evaluated to {@code true}.
+     */
+    public void assertEventsReceivedInHandler() {
+        int eventsOutsideHandler = mEventsReceivedOutsideHandler.get();
+        String message = String.format(
+                "Events arrived outside the associated Looper. Expected=0, Found=%d",
+                eventsOutsideHandler);
+        Assert.assertEquals(message, 0 /* expected */, eventsOutsideHandler);
+    }
+
+    /**
+     * Keeps track of the number of events that arrived in a different {@link Looper} than the one
+     * associated with the {@link TestSensorEventListener}.
+     */
+    private void checkHandler() {
+        if (mHandler != null && mHandler.getLooper() != Looper.myLooper()) {
+            mEventsReceivedOutsideHandler.incrementAndGet();
+        }
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
index dc40ff4..2468bd1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -19,35 +19,20 @@
 import junit.framework.Assert;
 
 import android.content.Context;
-import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
-import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
 import android.util.Log;
 
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CountDownLatch;
 
 /**
- * A test class that performs the actions of {@link SensorManager} on a single sensor. This
- * class allows for a single sensor to be registered and unregistered as well as performing
- * operations such as flushing the sensor events and gathering events. This class also manages
- * performing the test verifications for the sensor manager.
- * <p>
- * This class requires that operations are performed in the following order:
- * <p><ul>
- * <li>{@link #registerListener(TestSensorEventListener)}</li>
- * <li>{@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
- * <li>{@link #unregisterListener()}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, int)}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, long, TimeUnit)}</li>
- * </ul><p>
- * If methods are called outside of this order, they will print a warning to the log and then
- * return. Both {@link #runSensor(TestSensorEventListener, int)}} and
- * {@link #runSensor(TestSensorEventListener, long, TimeUnit)} will perform the appropriate
- * set up and tear down.
- * <p>
+ * A test class that performs the actions of {@link SensorManager} on a single sensor.
+ * This class allows for a single sensor to be registered and unregistered as well as performing
+ * operations such as flushing the sensor events and gathering events.
+ * This class also manages performing the test verifications for the sensor manager.
+ *
+ * NOTE: this class is expected to mirror {@link SensorManager} operations, and perform the
+ * required test verifications along with them.
  */
 public class TestSensorManager {
     private static final String LOG_TAG = "TestSensorManager";
@@ -55,7 +40,7 @@
     private final SensorManager mSensorManager;
     private final TestSensorEnvironment mEnvironment;
 
-    private TestSensorEventListener mTestSensorEventListener;
+    private volatile TestSensorEventListener mTestSensorEventListener;
 
     /**
      * @deprecated Use {@link #TestSensorManager(TestSensorEnvironment)} instead.
@@ -90,19 +75,45 @@
             return;
         }
 
-        mTestSensorEventListener = listener != null ? listener : new TestSensorEventListener();
-        mTestSensorEventListener.setEnvironment(mEnvironment);
-
+        mTestSensorEventListener = listener;
         String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
         boolean result = mSensorManager.registerListener(
                 mTestSensorEventListener,
                 mEnvironment.getSensor(),
                 mEnvironment.getRequestedSamplingPeriodUs(),
-                mEnvironment.getMaxReportLatencyUs());
+                mEnvironment.getMaxReportLatencyUs(),
+                mTestSensorEventListener.getHandler());
         Assert.assertTrue(message, result);
     }
 
     /**
+     * Register the listener. This method will perform a no-op if the sensor is already registered.
+     *
+     * @return A CountDownLatch initialized with eventCount which is used to wait for sensor
+     * events.
+     * @throws AssertionError if there was an error registering the listener with the
+     * {@link SensorManager}
+     */
+    public CountDownLatch registerListener(TestSensorEventListener listener, int eventCount) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return null;
+        }
+
+        CountDownLatch latch = listener.getLatchForSensorEvents(eventCount);
+        mTestSensorEventListener = listener;
+        String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
+        boolean result = mSensorManager.registerListener(
+                mTestSensorEventListener,
+                mEnvironment.getSensor(),
+                mEnvironment.getRequestedSamplingPeriodUs(),
+                mEnvironment.getMaxReportLatencyUs(),
+                mTestSensorEventListener.getHandler());
+        Assert.assertTrue(message, result);
+        return latch;
+    }
+
+    /**
      * Unregister the listener. This method will perform a no-op if the sensor is not registered.
      */
     public void unregisterListener() {
@@ -110,136 +121,27 @@
             Log.w(LOG_TAG, "No listener registered, returning.");
             return;
         }
-
-        mSensorManager.unregisterListener(
-                mTestSensorEventListener,
-                mEnvironment.getSensor());
+        mSensorManager.unregisterListener(mTestSensorEventListener, mEnvironment.getSensor());
+        mTestSensorEventListener.assertEventsReceivedInHandler();
         mTestSensorEventListener = null;
     }
 
     /**
-     * Wait for a specific number of events.
-     */
-    public void waitForEvents(int eventCount) throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            Log.w(LOG_TAG, "No listener registered, returning.");
-            return;
-        }
-        mTestSensorEventListener.waitForEvents(eventCount);
-    }
-
-    /**
-     * Wait for a specific duration.
-     */
-    public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            Log.w(LOG_TAG, "No listener registered, returning.");
-            return;
-        }
-        mTestSensorEventListener.waitForEvents(duration, timeUnit);
-    }
-
-    /**
      * Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
      * the sensor is not registered.
      *
-     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false
+     * @return A CountDownLatch which can be used to wait for a flush complete event.
+     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} fails.
      */
-    public void startFlush() {
+    public CountDownLatch requestFlush() {
         if (mTestSensorEventListener == null) {
-            return;
+            Log.w(LOG_TAG, "No listener registered, returning.");
+            return null;
         }
-
+        CountDownLatch latch = mTestSensorEventListener.getLatchForFlushCompleteEvent();
         Assert.assertTrue(
                 SensorCtsHelper.formatAssertionMessage("Flush", mEnvironment),
                 mSensorManager.flush(mTestSensorEventListener));
-    }
-
-    /**
-     * Wait for {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will
-     * perform a no-op if the sensor is not registered.
-     *
-     * @throws AssertionError if there is a time out
-     * @throws InterruptedException if the thread was interrupted
-     */
-    public void waitForFlushCompleted() throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            return;
-        }
-        mTestSensorEventListener.waitForFlushComplete();
-    }
-
-    /**
-     * Call {@link SensorManager#flush(SensorEventListener)} and wait for
-     * {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will perform
-     * a no-op if the sensor is not registered.
-     *
-     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false or
-     * if there is a time out
-     * @throws InterruptedException if the thread was interrupted
-     */
-    public void flush() throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            return;
-        }
-        startFlush();
-        waitForFlushCompleted();
-    }
-
-    /**
-     * Register a listener, wait for a specific number of events, and then unregister the listener.
-     */
-    public void runSensor(TestSensorEventListener listener, int eventCount)
-            throws InterruptedException {
-        if (mTestSensorEventListener != null) {
-            Log.w(LOG_TAG, "Listener already registered, returning.");
-            return;
-        }
-        try {
-            registerListener(listener);
-            waitForEvents(eventCount);
-        } finally {
-            unregisterListener();
-        }
-    }
-
-    /**
-     * Register a listener, wait for a specific duration, and then unregister the listener.
-     */
-    public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit)
-            throws InterruptedException {
-        if (mTestSensorEventListener != null) {
-            Log.w(LOG_TAG, "Listener already registered, returning.");
-            return;
-        }
-        try {
-            registerListener(listener);
-            waitForEvents(duration, timeUnit);
-        } finally {
-            unregisterListener();
-        }
-    }
-
-    /**
-     * Registers a listener, waits for a specific duration, calls flush, and waits for flush to
-     * complete.
-     */
-    public void runSensorAndFlush(
-            TestSensorEventListener listener,
-            long duration,
-            TimeUnit timeUnit) throws InterruptedException {
-        if (mTestSensorEventListener != null) {
-            Log.w(LOG_TAG, "Listener already registered, returning.");
-            return;
-        }
-
-        try {
-            registerListener(listener);
-            SensorCtsHelper.sleep(duration, timeUnit);
-            startFlush();
-            listener.waitForFlushComplete();
-        } finally {
-            unregisterListener();
-        }
+        return latch;
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
deleted file mode 100644
index 299f470..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
+++ /dev/null
@@ -1,82 +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.hardware.cts.helpers;
-
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
-import android.hardware.cts.helpers.sensorverification.ISensorVerification;
-
-import java.util.Collection;
-import java.util.LinkedList;
-
-/**
- * A {@link TestSensorEventListener} which performs validations on the received events on the fly.
- * This class is useful for long running tests where it is not practical to store all the events to
- * be processed after.
- */
-public class ValidatingSensorEventListener extends TestSensorEventListener {
-
-    private final Collection<ISensorVerification> mVerifications =
-            new LinkedList<ISensorVerification>();
-
-    /**
-     * Construct a {@link ValidatingSensorEventListener} with an additional
-     * {@link SensorEventListener2}.
-     */
-    public ValidatingSensorEventListener(SensorEventListener2 listener,
-            ISensorVerification ... verifications) {
-        super(listener);
-        for (ISensorVerification verification : verifications) {
-            mVerifications.add(verification);
-        }
-    }
-
-    /**
-     * Construct a {@link ValidatingSensorEventListener} with an additional
-     * {@link SensorEventListener2}.
-     */
-    public ValidatingSensorEventListener(SensorEventListener2 listener,
-            Collection<ISensorVerification> verifications) {
-        this(listener, verifications.toArray(new ISensorVerification[0]));
-    }
-
-    /**
-     * Construct a {@link ValidatingSensorEventListener}.
-     */
-    public ValidatingSensorEventListener(ISensorVerification ... verifications) {
-        this(null, verifications);
-    }
-
-    /**
-     * Construct a {@link ValidatingSensorEventListener}.
-     */
-    public ValidatingSensorEventListener(Collection<ISensorVerification> verifications) {
-        this(null, verifications);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        TestSensorEvent testEvent = new TestSensorEvent(event);
-        for (ISensorVerification verification : mVerifications) {
-            verification.addSensorEvent(testEvent);
-        }
-        super.onSensorChanged(event);
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java b/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java
new file mode 100644
index 0000000..d34c0af
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java
@@ -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.
+ */
+
+package android.hardware.cts.helpers.reporting;
+
+import android.hardware.cts.helpers.SensorTestPlatformException;
+
+/**
+ * Interface that represents a node in a hierarchy built by the sensor test platform.
+ */
+// TODO: this is an intermediate state to introduce a full-blown centralized recorder data produced
+//       by sensor tests
+public interface ISensorTestNode {
+
+    /**
+     * Provides a name (tag) that can be used to identify the current node.
+     */
+    String getName() throws SensorTestPlatformException;
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
deleted file mode 100644
index 5b969f2..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
+++ /dev/null
@@ -1,60 +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.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * A {@link ISensorOperation} which contains a common implementation for gathering
- * {@link SensorStats}.
- */
-public abstract class AbstractSensorOperation implements ISensorOperation {
-
-    private final SensorStats mStats = new SensorStats();
-
-    /**
-     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
-     */
-    protected void addSensorStats(String key, SensorStats stats) {
-        mStats.addSensorStats(key, stats);
-    }
-
-    /**
-     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
-     * to be added. This is useful for {@link ISensorOperation}s that have many iterations or child
-     * operations. The key added is in the form {@code key + "_" + index} where index may be zero
-     * padded.
-     */
-    protected void addSensorStats(String key, int index, SensorStats stats) {
-        addSensorStats(String.format("%s_%03d", key, index), stats);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public SensorStats getStats() {
-        return mStats;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public abstract ISensorOperation clone();
-
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
index 88e4954..436a7cf 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -22,14 +22,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 
 import java.util.concurrent.TimeUnit;
 
 /**
- * An {@link ISensorOperation} which performs another {@link ISensorOperation} and then wakes up
+ * An {@link SensorOperation} which performs another {@link SensorOperation} and then wakes up
  * after a specified period of time and waits for the child operation to complete.
  * <p>
  * This operation can be used to allow the device to go to sleep and wake it up after a specified
@@ -40,11 +40,11 @@
  * but wake the device one time at the specified period.
  * </p>
  */
-public class AlarmOperation extends AbstractSensorOperation {
+public class AlarmOperation extends SensorOperation {
     private static final String ACTION = "AlarmOperationAction";
     private static final String WAKE_LOCK_TAG = "AlarmOperationWakeLock";
 
-    private final ISensorOperation mOperation;
+    private final SensorOperation mOperation;
     private final Context mContext;
     private final long mSleepDuration;
     private final TimeUnit mTimeUnit;
@@ -55,13 +55,17 @@
     /**
      * Constructor for {@link DelaySensorOperation}
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param context the context used to access the alarm manager
      * @param sleepDuration the amount of time to sleep
      * @param timeUnit the unit of the duration
      */
-    public AlarmOperation(ISensorOperation operation, Context context, long sleepDuration,
+    public AlarmOperation(
+            SensorOperation operation,
+            Context context,
+            long sleepDuration,
             TimeUnit timeUnit) {
+        super(operation.getStats());
         mOperation = operation;
         mContext = context;
         mSleepDuration = sleepDuration;
@@ -72,7 +76,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
         // Start alarm
         IntentFilter intentFilter = new IntentFilter(ACTION);
         BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -92,7 +96,7 @@
 
         // Execute operation
         try {
-            mOperation.execute();
+            mOperation.execute(asTestNode(parent));
         } finally {
             releaseWakeLock();
         }
@@ -102,14 +106,6 @@
      * {@inheritDoc}
      */
     @Override
-    public SensorStats getStats() {
-        return mOperation.getStats();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public AlarmOperation clone() {
         return new AlarmOperation(mOperation, mContext, mSleepDuration, mTimeUnit);
     }
@@ -120,7 +116,7 @@
      */
     private synchronized void acquireWakeLock() {
         // Don't acquire wake lock if the operation has already completed.
-        if (mCompleted == true || mWakeLock != null) {
+        if (mCompleted || mWakeLock != null) {
             return;
         }
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
index b4d1f23..8c52222 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -17,30 +17,28 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import java.util.concurrent.TimeUnit;
 
 /**
- * An {@link ISensorOperation} which delays for a specified period of time before performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which delays for a specified period of time before performing another
+ * {@link SensorOperation}.
  */
-public class DelaySensorOperation implements ISensorOperation {
-    private final ISensorOperation mOperation;
+public class DelaySensorOperation extends SensorOperation {
+    private final SensorOperation mOperation;
     private final long mDelay;
     private final TimeUnit mTimeUnit;
 
     /**
      * Constructor for {@link DelaySensorOperation}
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param delay the amount of time to delay
      * @param timeUnit the unit of the delay
      */
-    public DelaySensorOperation(ISensorOperation operation, long delay, TimeUnit timeUnit) {
-        if (operation == null || timeUnit == null) {
-            throw new IllegalArgumentException("Arguments cannot be null");
-        }
+    public DelaySensorOperation(SensorOperation operation, long delay, TimeUnit timeUnit) {
+        super(operation.getStats());
         mOperation = operation;
         mDelay = delay;
         mTimeUnit = timeUnit;
@@ -50,17 +48,9 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
         SensorCtsHelper.sleep(mDelay, mTimeUnit);
-        mOperation.execute();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public SensorStats getStats() {
-        return mOperation.getStats();
+        mOperation.execute(asTestNode(parent));
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
index bb64dfa..238956b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -17,16 +17,17 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import junit.framework.Assert;
 
 import java.util.concurrent.TimeUnit;
 
 /**
- * A fake {@ISensorOperation} that will run for a specified time and then pass or fail. Useful when
- * debugging the framework.
+ * A fake {@link SensorOperation} that will run for a specified time and then pass or fail. Useful
+ * when debugging the framework.
  */
-public class FakeSensorOperation extends AbstractSensorOperation {
+public class FakeSensorOperation extends SensorOperation {
     private static final int NANOS_PER_MILLI = 1000000;
 
     private final boolean mFail;
@@ -56,7 +57,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() {
+    public void execute(ISensorTestNode parent) {
         long delayNs = TimeUnit.NANOSECONDS.convert(mDelay, mTimeUnit);
         try {
             Thread.sleep(delayNs / NANOS_PER_MILLI, (int) (delayNs % NANOS_PER_MILLI));
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
deleted file mode 100644
index 62a4e9e..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
+++ /dev/null
@@ -1,63 +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.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * Interface used by all sensor operations. This allows for complex operations such as chaining
- * operations together or running operations in parallel.
- * <p>
- * Certain restrictions exist for {@link ISensorOperation}s:
- * <p><ul>
- * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
- * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
- * synchronization for operations.</li>
- * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
- * the returned value is undefined.</li>
- * <li>{@link #clone()} may be called any time and should return an operation with the same
- * parameters as the original.</li>
- * </ul>
- */
-public interface ISensorOperation {
-
-    /**
-     * Executes the sensor operation.  This may throw {@link RuntimeException}s such as
-     * {@link AssertionError}s.
-     *
-     * NOTE: the operation is expected to handle interruption by:
-     * - cleaning up on {@link InterruptedException}
-     * - propagating the exception down the stack
-     */
-    public void execute() throws InterruptedException;
-
-    /**
-     * Get the stats for the operation.
-     *
-     * @return The {@link SensorStats} for the operation.
-     */
-    public SensorStats getStats();
-
-    /**
-     * Clones the {@link ISensorOperation}. The implementation should also clone all child
-     * operations, so that a cloned operation will run with the exact same parameters as the
-     * original. The stats should not be cloned.
-     *
-     * @return The cloned {@link ISensorOperation}
-     */
-    public ISensorOperation clone();
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
index 5a4466c..ed70b70 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -19,10 +19,10 @@
 import junit.framework.Assert;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.os.SystemClock;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -33,14 +33,14 @@
 import java.util.concurrent.TimeoutException;
 
 /**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in parallel.
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in parallel.
  * The children are run in parallel but are given an index label in the order they are added. This
- * class can be combined to compose complex {@link ISensorOperation}s.
+ * class can be combined to compose complex {@link SensorOperation}s.
  */
-public class ParallelSensorOperation extends AbstractSensorOperation {
+public class ParallelSensorOperation extends SensorOperation {
     public static final String STATS_TAG = "parallel";
 
-    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+    private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
     private final Long mTimeout;
     private final TimeUnit mTimeUnit;
 
@@ -65,10 +65,10 @@
     }
 
     /**
-     * Add a set of {@link ISensorOperation}s.
+     * Add a set of {@link SensorOperation}s.
      */
-    public void add(ISensorOperation ... operations) {
-        for (ISensorOperation operation : operations) {
+    public void add(SensorOperation ... operations) {
+        for (SensorOperation operation : operations) {
             if (operation == null) {
                 throw new IllegalArgumentException("Arguments cannot be null");
             }
@@ -77,11 +77,11 @@
     }
 
     /**
-     * Executes the {@link ISensorOperation}s in parallel. If an exception occurs one or more
+     * Executes the {@link SensorOperation}s in parallel. If an exception occurs one or more
      * operations, the first exception will be thrown once all operations are completed.
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(final ISensorTestNode parent) throws InterruptedException {
         int operationsCount = mOperations.size();
         ThreadPoolExecutor executor = new ThreadPoolExecutor(
                 operationsCount,
@@ -92,12 +92,13 @@
         executor.allowCoreThreadTimeOut(true);
         executor.prestartAllCoreThreads();
 
-        ArrayList<Future<ISensorOperation>> futures = new ArrayList<Future<ISensorOperation>>();
-        for (final ISensorOperation operation : mOperations) {
-            Future<ISensorOperation> future = executor.submit(new Callable<ISensorOperation>() {
+        final ISensorTestNode currentNode = asTestNode(parent);
+        ArrayList<Future<SensorOperation>> futures = new ArrayList<>();
+        for (final SensorOperation operation : mOperations) {
+            Future<SensorOperation> future = executor.submit(new Callable<SensorOperation>() {
                 @Override
-                public ISensorOperation call() throws Exception {
-                    operation.execute();
+                public SensorOperation call() throws Exception {
+                    operation.execute(currentNode);
                     return operation;
                 }
             });
@@ -111,12 +112,12 @@
         }
 
         boolean hasAssertionErrors = false;
-        ArrayList<Integer> timeoutIndices = new ArrayList<Integer>();
-        ArrayList<Throwable> exceptions = new ArrayList<Throwable>();
+        ArrayList<Integer> timeoutIndices = new ArrayList<>();
+        ArrayList<Throwable> exceptions = new ArrayList<>();
         for (int i = 0; i < operationsCount; ++i) {
-            Future<ISensorOperation> future = futures.get(i);
+            Future<SensorOperation> future = futures.get(i);
             try {
-                ISensorOperation operation = getFutureResult(future, executionTimeNs);
+                SensorOperation operation = getFutureResult(future, executionTimeNs);
                 addSensorStats(STATS_TAG, i, operation.getStats());
             } catch (ExecutionException e) {
                 // extract the exception thrown by the worker thread
@@ -151,7 +152,7 @@
     @Override
     public ParallelSensorOperation clone() {
         ParallelSensorOperation operation = new ParallelSensorOperation();
-        for (ISensorOperation subOperation : mOperations) {
+        for (SensorOperation subOperation : mOperations) {
             operation.add(subOperation.clone());
         }
         return operation;
@@ -160,7 +161,7 @@
     /**
      * Helper method that waits for a {@link Future} to complete, and returns its result.
      */
-    private ISensorOperation getFutureResult(Future<ISensorOperation> future, Long timeoutNs)
+    private SensorOperation getFutureResult(Future<SensorOperation> future, Long timeoutNs)
             throws ExecutionException, TimeoutException, InterruptedException {
         if (timeoutNs == null) {
             return future.get();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
index 3d682fe..5b333b8 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -17,42 +17,44 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 /**
- * A {@link ISensorOperation} that executes a single {@link ISensorOperation} a given number of
- * times. This class can be combined to compose complex {@link ISensorOperation}s.
+ * A {@link SensorOperation} that executes a single {@link SensorOperation} a given number of
+ * times. This class can be combined to compose complex {@link SensorOperation}s.
  */
-public class RepeatingSensorOperation extends AbstractSensorOperation {
+public class RepeatingSensorOperation extends SensorOperation {
     public static final String STATS_TAG = "repeating";
 
-    private final ISensorOperation mOperation;
+    private final SensorOperation mOperation;
     private final int mIterations;
 
     /**
      * Constructor for {@link RepeatingSensorOperation}.
      *
-     * @param operation the {@link ISensorOperation} to run.
+     * @param operation the {@link SensorOperation} to run.
      * @param iterations the number of iterations to run the operation for.
      */
-    public RepeatingSensorOperation(ISensorOperation operation, int iterations) {
+    public RepeatingSensorOperation(SensorOperation operation, int iterations) {
         if (operation == null) {
             throw new IllegalArgumentException("Arguments cannot be null");
         }
         mOperation = operation;
         mIterations = iterations;
-
     }
 
     /**
-     * Executes the {@link ISensorOperation}s the given number of times. If an exception occurs
-     * in one iterations, it is thrown and all subsequent iterations will not run.
+     * Executes the {@link SensorOperation}s the given number of times. If an exception occurs in
+     * one iterations, it is thrown and all subsequent iterations will not run.
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
+        ISensorTestNode currentNode = asTestNode(parent);
         for(int i = 0; i < mIterations; ++i) {
-            ISensorOperation operation = mOperation.clone();
+            SensorOperation operation = mOperation.clone();
             try {
-                operation.execute();
+                operation.execute(new TestNode(currentNode, i));
             } catch (AssertionError e) {
                 String msg = String.format("Iteration %d failed: \"%s\"", i, e.getMessage());
                 getStats().addValue(SensorStats.ERROR, msg);
@@ -70,4 +72,19 @@
     public RepeatingSensorOperation clone() {
         return new RepeatingSensorOperation(mOperation.clone(), mIterations);
     }
+
+    private class TestNode implements ISensorTestNode {
+        private final ISensorTestNode mTestNode;
+        private final int mIteration;
+
+        public TestNode(ISensorTestNode parent, int iteration) {
+            mTestNode = asTestNode(parent);
+            mIteration = iteration;
+        }
+
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return String.format("%s-iteration%d", mTestNode.getName(), mIteration);
+        }
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
new file mode 100644
index 0000000..66604d3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
@@ -0,0 +1,110 @@
+/*
+ * 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.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+
+/**
+ * Base class used by all sensor operations. This allows for complex operations such as chaining
+ * operations together or running operations in parallel.
+ * <p>
+ * Certain restrictions exist for {@link SensorOperation}s:
+ * <p><ul>
+ * <li>{@link #execute(ISensorTestNode)} should only be called once and behavior is undefined for
+ * subsequent calls.
+ * Once {@link #execute(ISensorTestNode)} is called, the class should not be modified. Generally,
+ * there is no synchronization for operations.</li>
+ * <li>{@link #getStats()} should only be called after {@link #execute(ISensorTestNode)}. If it
+ * is called before, the returned value is undefined.</li>
+ * <li>{@link #clone()} may be called any time and should return an operation with the same
+ * parameters as the original.</li>
+ * </ul>
+ */
+public abstract class SensorOperation {
+    private final SensorStats mStats;
+
+    protected SensorOperation() {
+        this(new SensorStats());
+    }
+
+    protected SensorOperation(SensorStats stats) {
+        mStats = stats;
+    }
+
+    /**
+     * @return The {@link SensorStats} for the operation.
+     */
+    public SensorStats getStats() {
+        return mStats;
+    }
+
+    /**
+     * Executes the sensor operation.
+     * This may throw {@link RuntimeException}s such as {@link AssertionError}s.
+     *
+     * NOTE: the operation is expected to handle interruption by:
+     * - cleaning up on {@link InterruptedException}
+     * - propagating the exception down the stack
+     */
+    public abstract void execute(ISensorTestNode parent) throws InterruptedException;
+
+    /**
+     * @return The cloned {@link SensorOperation}.
+     *
+     * NOTE: The implementation should also clone all child operations, so that a cloned operation
+     * will run with the exact same parameters as the original. The stats should not be cloned.
+     */
+    public abstract SensorOperation clone();
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
+     */
+    protected void addSensorStats(String key, SensorStats stats) {
+        getStats().addSensorStats(key, stats);
+    }
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
+     * to be added. This is useful for {@link SensorOperation}s that have many iterations or child
+     * operations. The key added is in the form {@code key + "_" + index} where index may be zero
+     * padded.
+     */
+    protected void addSensorStats(String key, int index, SensorStats stats) {
+        addSensorStats(String.format("%s_%03d", key, index), stats);
+    }
+
+    protected ISensorTestNode asTestNode(ISensorTestNode parent) {
+        return new SensorTestNode(parent, this);
+    }
+
+    private class SensorTestNode implements ISensorTestNode {
+        private final ISensorTestNode mParent;
+        private final SensorOperation mOperation;
+
+        public SensorTestNode(ISensorTestNode parent, SensorOperation operation) {
+            mParent = parent;
+            mOperation = operation;
+        }
+
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return mParent.getName() + "-" + mOperation.getClass().getSimpleName();
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
index bc48725..30da9a0 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -19,18 +19,27 @@
 import junit.framework.TestCase;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /**
- * Tests for the primitive {@link ISensorOperation}s including {@link DelaySensorOperation},
+ * Tests for the primitive {@link SensorOperation}s including {@link DelaySensorOperation},
  * {@link ParallelSensorOperation}, {@link RepeatingSensorOperation} and
  * {@link SequentialSensorOperation}.
  */
 public class SensorOperationTest extends TestCase {
     private static final long TEST_DURATION_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(5);
 
+    private final ISensorTestNode mTestNode = new ISensorTestNode() {
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return "SensorOperationUnitTest";
+        }
+    };
+
     /**
      * Test that the {@link FakeSensorOperation} functions correctly. Other tests in this class
      * rely on this operation.
@@ -38,18 +47,18 @@
     public void testFakeSensorOperation() throws InterruptedException {
         final int opDurationMs = 100;
 
-        ISensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
+        SensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
 
         assertFalse(op.getStats().flatten().containsKey("executed"));
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long duration = System.currentTimeMillis() - start;
         assertTrue(Math.abs(opDurationMs - duration) < TEST_DURATION_THRESHOLD_MS);
         assertTrue(op.getStats().flatten().containsKey("executed"));
 
         op = new FakeSensorOperation(true, 0, TimeUnit.MILLISECONDS);
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -65,12 +74,12 @@
         final int subOpDurationMs = 100;
 
         FakeSensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
-        ISensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
+        SensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
 
         long startMs = System.currentTimeMillis();
-        op.execute();
-        long dirationMs = System.currentTimeMillis() - startMs;
-        long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - dirationMs);
+        op.execute(mTestNode);
+        long durationMs = System.currentTimeMillis() - startMs;
+        long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - durationMs);
         assertTrue(durationDeltaMs < TEST_DURATION_THRESHOLD_MS);
     }
 
@@ -83,7 +92,7 @@
 
         ParallelSensorOperation op = new ParallelSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
-            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+            SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
                     TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
@@ -92,7 +101,7 @@
         assertEquals(0, statsKeys.size());
 
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long durationMs = System.currentTimeMillis() - start;
         long durationDeltaMs = Math.abs(subOpDurationMs - durationMs);
         String message = String.format(
@@ -124,7 +133,7 @@
         ParallelSensorOperation op = new ParallelSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
             // Trigger failures in the 5th, 55th operations at t=5ms, t=55ms
-            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
+            SensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
 
@@ -132,7 +141,7 @@
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -164,7 +173,7 @@
         ParallelSensorOperation op = new ParallelSensorOperation(1, TimeUnit.SECONDS);
         for (int i = 0; i < subOpCount; i++) {
             // Trigger timeouts in the 5th, 55th operations (5 seconds vs 1 seconds)
-            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
+            SensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
             op.add(subOp);
         }
 
@@ -172,7 +181,7 @@
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -196,14 +205,14 @@
         final int iterations = 10;
         final int subOpDurationMs = 100;
 
-        ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
-        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+        SensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+        SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
 
         Set<String> statsKeys = op.getStats().flatten().keySet();
         assertEquals(0, statsKeys.size());
 
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long duration = System.currentTimeMillis() - start;
         assertTrue(Math.abs(subOpDurationMs * iterations - duration) < TEST_DURATION_THRESHOLD_MS);
 
@@ -223,13 +232,13 @@
         final int iterations = 100;
         final int failCount = 75;
 
-        ISensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
+        SensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
             private int mExecutedCount = 0;
             private SensorStats mFakeStats = new SensorStats();
 
             @Override
-            public void execute() {
-                super.execute();
+            public void execute(ISensorTestNode parent) {
+                super.execute(parent);
                 mExecutedCount++;
 
                 if (failCount == mExecutedCount) {
@@ -249,13 +258,13 @@
                 return mFakeStats;
             }
         };
-        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+        SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
 
         Set<String> statsKeys = op.getStats().flatten().keySet();
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -283,7 +292,7 @@
 
         SequentialSensorOperation op = new SequentialSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
-            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+            SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
                     TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
@@ -292,7 +301,7 @@
         assertEquals(0, statsKeys.size());
 
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long duration = System.currentTimeMillis() - start;
         assertTrue(Math.abs(subOpDurationMs * subOpCount - duration) < TEST_DURATION_THRESHOLD_MS);
 
@@ -315,7 +324,7 @@
         SequentialSensorOperation op = new SequentialSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
             // Trigger a failure in the 75th operation only
-            ISensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
+            SensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
                     TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
@@ -324,7 +333,7 @@
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
index 2ed0ca6..847c0f2 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -17,25 +17,25 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
-import java.util.LinkedList;
-import java.util.List;
+import java.util.ArrayList;
 
 /**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in a
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in a
  * sequence. The children are executed in the order they are added. This class can be combined to
- * compose complex {@link ISensorOperation}s.
+ * compose complex {@link SensorOperation}s.
  */
-public class SequentialSensorOperation extends AbstractSensorOperation {
+public class SequentialSensorOperation extends SensorOperation {
     public static final String STATS_TAG = "sequential";
 
-    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+    private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
 
     /**
-     * Add a set of {@link ISensorOperation}s.
+     * Add a set of {@link SensorOperation}s.
      */
-    public void add(ISensorOperation ... operations) {
-        for (ISensorOperation operation : operations) {
+    public void add(SensorOperation ... operations) {
+        for (SensorOperation operation : operations) {
             if (operation == null) {
                 throw new IllegalArgumentException("Arguments cannot be null");
             }
@@ -44,15 +44,16 @@
     }
 
     /**
-     * Executes the {@link ISensorOperation}s in the order they were added. If an exception occurs
+     * Executes the {@link SensorOperation}s in the order they were added. If an exception occurs
      * in one operation, it is thrown and all subsequent operations will not run.
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
+        ISensorTestNode currentNode = asTestNode(parent);
         for (int i = 0; i < mOperations.size(); i++) {
-            ISensorOperation operation = mOperations.get(i);
+            SensorOperation operation = mOperations.get(i);
             try {
-                operation.execute();
+                operation.execute(currentNode);
             } catch (AssertionError e) {
                 String msg = String.format("Operation %d failed: \"%s\"", i, e.getMessage());
                 getStats().addValue(SensorStats.ERROR, msg);
@@ -69,7 +70,7 @@
     @Override
     public SequentialSensorOperation clone() {
         SequentialSensorOperation operation = new SequentialSensorOperation();
-        for (ISensorOperation subOperation : mOperations) {
+        for (SensorOperation subOperation : mOperations) {
             operation.add(subOperation.clone());
         }
         return operation;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
deleted file mode 100644
index d5aa4b9..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
+++ /dev/null
@@ -1,67 +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.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
- * <p>
- * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
- * events, call flush, and then run all the tests.
- * </p>
- */
-public class TestSensorFlushOperation extends VerifiableSensorOperation {
-    private final Long mDuration;
-    private final TimeUnit mTimeUnit;
-
-    /**
-     * Create a {@link TestSensorOperation}.
-     *
-     * @param environment the test environment
-     * @param duration the duration to gather events before calling {@code SensorManager.flush()}
-     * @param timeUnit the time unit of the duration
-     */
-    public TestSensorFlushOperation(
-            TestSensorEnvironment environment,
-            long duration,
-            TimeUnit timeUnit) {
-        super(environment);
-        mDuration = duration;
-        mTimeUnit = timeUnit;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
-        mSensorManager.runSensorAndFlush(listener, mDuration, mTimeUnit);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected VerifiableSensorOperation doClone() {
-        return new TestSensorFlushOperation(mEnvironment, mDuration,mTimeUnit);
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 695e1a7..3b90b15 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -16,83 +16,265 @@
 
 package android.hardware.cts.helpers.sensoroperations;
 
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
+import junit.framework.Assert;
 
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
+import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+import android.hardware.cts.helpers.sensorverification.JitterVerification;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
+import android.hardware.cts.helpers.sensorverification.MeanVerification;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
+import android.os.Handler;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
+ * A {@link SensorOperation} used to verify that sensor events and sensor values are correct.
  * <p>
  * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
- * events and then run all the tests.
+ * depending on sensor type.  When {{@link #execute(ISensorTestNode)} is called, the sensor will
+ * collect the events and then run all the tests.
  * </p>
  */
-public class TestSensorOperation extends VerifiableSensorOperation {
-    private final Integer mEventCount;
-    private final Long mDuration;
-    private final TimeUnit mTimeUnit;
+public class TestSensorOperation extends SensorOperation {
+    private static final String TAG = "TestSensorOperation";
+
+    private final HashSet<ISensorVerification> mVerifications = new HashSet<>();
+
+    private final TestSensorManager mSensorManager;
+    private final TestSensorEnvironment mEnvironment;
+    private final Executor mExecutor;
+    private final Handler mHandler;
 
     /**
-     * Create a {@link TestSensorOperation}.
-     *
-     * @param environment the test environment
-     * @param eventCount the number of events to gather
+     * An interface that defines an abstraction for operations to be performed by the
+     * {@link TestSensorOperation}.
      */
-    public TestSensorOperation(TestSensorEnvironment environment, int eventCount) {
-        this(environment, eventCount, null /* duration */, null /* timeUnit */);
+    public interface Executor {
+        void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                throws InterruptedException;
     }
 
     /**
      * Create a {@link TestSensorOperation}.
-     *
-     * @param environment the test environment
-     * @param duration the duration to gather events for
-     * @param timeUnit the time unit of the duration
+     */
+    public TestSensorOperation(TestSensorEnvironment environment, Executor executor) {
+        this(environment, executor, null /* handler */);
+    }
+
+    /**
+     * Create a {@link TestSensorOperation}.
      */
     public TestSensorOperation(
             TestSensorEnvironment environment,
-            long duration,
-            TimeUnit timeUnit) {
-        this(environment, null /* eventCount */, duration, timeUnit);
+            Executor executor,
+            Handler handler) {
+        mEnvironment = environment;
+        mExecutor = executor;
+        mHandler = handler;
+        mSensorManager = new TestSensorManager(mEnvironment);
     }
 
     /**
-     * Private helper constructor.
+     * Set all of the default test expectations.
      */
-    private TestSensorOperation(
+    public void addDefaultVerifications() {
+        addVerification(EventGapVerification.getDefault(mEnvironment));
+        addVerification(EventOrderingVerification.getDefault(mEnvironment));
+        addVerification(FrequencyVerification.getDefault(mEnvironment));
+        addVerification(JitterVerification.getDefault(mEnvironment));
+        addVerification(MagnitudeVerification.getDefault(mEnvironment));
+        addVerification(MeanVerification.getDefault(mEnvironment));
+        addVerification(StandardDeviationVerification.getDefault(mEnvironment));
+        addVerification(EventTimestampSynchronizationVerification.getDefault(mEnvironment));
+    }
+
+    public void addVerification(ISensorVerification verification) {
+        if (verification != null) {
+            mVerifications.add(verification);
+        }
+    }
+
+    /**
+     * Collect the specified number of events from the sensor and run all enabled verifications.
+     */
+    @Override
+    public void execute(ISensorTestNode parent) throws InterruptedException {
+        getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
+        TestSensorEventListener listener = new TestSensorEventListener(mEnvironment, mHandler);
+        mExecutor.execute(mSensorManager, listener);
+
+        boolean failed = false;
+        StringBuilder sb = new StringBuilder();
+        List<TestSensorEvent> collectedEvents = listener.getCollectedEvents();
+        for (ISensorVerification verification : mVerifications) {
+            failed |= evaluateResults(collectedEvents, verification, sb);
+        }
+
+        if (failed) {
+            trySaveCollectedEvents(parent, listener);
+
+            String msg = SensorCtsHelper
+                    .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
+            getStats().addValue(SensorStats.ERROR, msg);
+            Assert.fail(msg);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public TestSensorOperation clone() {
+        TestSensorOperation operation = new TestSensorOperation(mEnvironment, mExecutor);
+        for (ISensorVerification verification : mVerifications) {
+            operation.addVerification(verification.clone());
+        }
+        return operation;
+    }
+
+    /**
+     * Evaluate the results of a test, aggregate the stats, and build the error message.
+     */
+    private boolean evaluateResults(
+            List<TestSensorEvent> events,
+            ISensorVerification verification,
+            StringBuilder sb) {
+        try {
+            // this is an intermediate state in refactoring, at some point verifications might
+            // become stateless
+            verification.addSensorEvents(events);
+            verification.verify(mEnvironment, getStats());
+        } catch (AssertionError e) {
+            if (sb.length() > 0) {
+                sb.append(", ");
+            }
+            sb.append(e.getMessage());
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Tries to save collected {@link TestSensorEvent}s to a file.
+     *
+     * NOTE: it is more important to handle verifications and its results, than failing if the file
+     * cannot be created. So we silently fail if necessary.
+     */
+    private void trySaveCollectedEvents(ISensorTestNode parent, TestSensorEventListener listener) {
+        String sanitizedFileName;
+        try {
+            String fileName = asTestNode(parent).getName();
+            sanitizedFileName = String.format(
+                    "%s-%s-%s_%dus.txt",
+                    SensorCtsHelper.sanitizeStringForFileName(fileName),
+                    SensorStats.getSanitizedSensorName(mEnvironment.getSensor()),
+                    mEnvironment.getFrequencyString(),
+                    mEnvironment.getMaxReportLatencyUs());
+        } catch (SensorTestPlatformException e) {
+            Log.w(TAG, "Unable to generate file name to save collected events", e);
+            return;
+        }
+
+        try {
+            listener.logCollectedEventsToFile(sanitizedFileName);
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to save collected events to file: " + sanitizedFileName, e);
+        }
+    }
+
+    /**
+     * Creates an operation that will wait for a given amount of events to arrive.
+     *
+     * @param environment The test environment.
+     * @param eventCount The number of events to wait for.
+     */
+    public static TestSensorOperation createOperation(
             TestSensorEnvironment environment,
-            Integer eventCount,
-            Long duration,
-            TimeUnit timeUnit) {
-        super(environment);
-        mEventCount = eventCount;
-        mDuration = duration;
-        mTimeUnit = timeUnit;
+            final int eventCount) {
+        Executor executor = new Executor() {
+            @Override
+            public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                    throws InterruptedException {
+                try {
+                    CountDownLatch latch = sensorManager.registerListener(listener, eventCount);
+                    listener.waitForEvents(latch, eventCount);
+                } finally {
+                    sensorManager.unregisterListener();
+                }
+            }
+        };
+        return new TestSensorOperation(environment, executor);
     }
 
     /**
-     * {@inheritDoc}
+     * Creates an operation that will wait for a given amount of time to collect events.
+     *
+     * @param environment The test environment.
+     * @param duration The duration to wait for events.
+     * @param timeUnit The time unit for {@code duration}.
      */
-    @Override
-    protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
-        if (mEventCount != null) {
-            mSensorManager.runSensor(listener, mEventCount);
-        } else {
-            mSensorManager.runSensor(listener, mDuration, mTimeUnit);
-        }
+    public static TestSensorOperation createOperation(
+            TestSensorEnvironment environment,
+            final long duration,
+            final TimeUnit timeUnit) {
+        Executor executor = new Executor() {
+            @Override
+            public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                    throws InterruptedException {
+                try {
+                    sensorManager.registerListener(listener);
+                    listener.waitForEvents(duration, timeUnit);
+                } finally {
+                    sensorManager.unregisterListener();
+                }
+            }
+        };
+        return new TestSensorOperation(environment, executor);
     }
 
     /**
-     * {@inheritDoc}
+     * Creates an operation that will wait for a given amount of time before calling
+     * {@link TestSensorManager#requestFlush()}.
+     *
+     * @param environment The test environment.
+     * @param duration The duration to wait before calling {@link TestSensorManager#requestFlush()}.
+     * @param timeUnit The time unit for {@code duration}.
      */
-    @Override
-    protected VerifiableSensorOperation doClone() {
-        if (mEventCount != null) {
-            return new TestSensorOperation(mEnvironment, mEventCount);
-        } else {
-            return new TestSensorOperation(mEnvironment, mDuration, mTimeUnit);
-        }
+    public static TestSensorOperation createFlushOperation(
+            TestSensorEnvironment environment,
+            final long duration,
+            final TimeUnit timeUnit) {
+        Executor executor = new Executor() {
+            @Override
+            public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                    throws InterruptedException {
+                try {
+                    sensorManager.registerListener(listener);
+                    SensorCtsHelper.sleep(duration, timeUnit);
+                    CountDownLatch latch = sensorManager.requestFlush();
+                    listener.waitForFlushComplete(latch);
+                } finally {
+                    sensorManager.unregisterListener();
+                }
+            }
+        };
+        return new TestSensorOperation(environment, executor);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
deleted file mode 100644
index 57018eb..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
+++ /dev/null
@@ -1,156 +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.hardware.cts.helpers.sensoroperations;
-
-import junit.framework.Assert;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorStats;
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
-import android.hardware.cts.helpers.TestSensorManager;
-import android.hardware.cts.helpers.ValidatingSensorEventListener;
-import android.hardware.cts.helpers.sensorverification.EventGapVerification;
-import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
-import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
-import android.hardware.cts.helpers.sensorverification.ISensorVerification;
-import android.hardware.cts.helpers.sensorverification.JitterVerification;
-import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
-import android.hardware.cts.helpers.sensorverification.MeanVerification;
-import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
-
-import java.util.Collection;
-import java.util.HashSet;
-
-/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
- * <p>
- * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
- * events and then run all the tests.
- * </p>
- */
-public abstract class VerifiableSensorOperation extends AbstractSensorOperation {
-    protected final TestSensorManager mSensorManager;
-    protected final TestSensorEnvironment mEnvironment;
-
-    private final Collection<ISensorVerification> mVerifications =
-            new HashSet<ISensorVerification>();
-
-    private boolean mLogEvents = false;
-
-    /**
-     * Create a {@link TestSensorOperation}.
-     *
-     * @param environment the test environment
-     */
-    public VerifiableSensorOperation(TestSensorEnvironment environment) {
-        mEnvironment = environment;
-        mSensorManager = new TestSensorManager(mEnvironment);
-    }
-
-    /**
-     * Set whether to log events.
-     */
-    public void setLogEvents(boolean logEvents) {
-        mLogEvents = logEvents;
-    }
-
-    /**
-     * Set all of the default test expectations.
-     */
-    public void addDefaultVerifications() {
-        addVerification(EventGapVerification.getDefault(mEnvironment));
-        addVerification(EventOrderingVerification.getDefault(mEnvironment));
-        addVerification(FrequencyVerification.getDefault(mEnvironment));
-        addVerification(JitterVerification.getDefault(mEnvironment));
-        addVerification(MagnitudeVerification.getDefault(mEnvironment));
-        addVerification(MeanVerification.getDefault(mEnvironment));
-        // Skip SigNumVerification since it has no default
-        addVerification(StandardDeviationVerification.getDefault(mEnvironment));
-    }
-
-    public void addVerification(ISensorVerification verification) {
-        if (verification != null) {
-            mVerifications.add(verification);
-        }
-    }
-
-    /**
-     * Collect the specified number of events from the sensor and run all enabled verifications.
-     */
-    @Override
-    public void execute() throws InterruptedException {
-        getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
-
-        ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
-        listener.setLogEvents(mLogEvents);
-
-        doExecute(listener);
-
-        boolean failed = false;
-        StringBuilder sb = new StringBuilder();
-        for (ISensorVerification verification : mVerifications) {
-            failed |= evaluateResults(verification, sb);
-        }
-
-        if (failed) {
-            String msg = SensorCtsHelper
-                    .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
-            getStats().addValue(SensorStats.ERROR, msg);
-            Assert.fail(msg);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public VerifiableSensorOperation clone() {
-        VerifiableSensorOperation operation = doClone();
-        for (ISensorVerification verification : mVerifications) {
-            operation.addVerification(verification.clone());
-        }
-        return operation;
-    }
-
-    /**
-     * Execute operations in a {@link TestSensorManager}.
-     */
-    protected abstract void doExecute(TestSensorEventListener listener) throws InterruptedException;
-
-    /**
-     * Clone the subclass operation.
-     */
-    protected abstract VerifiableSensorOperation doClone();
-
-    /**
-     * Evaluate the results of a test, aggregate the stats, and build the error message.
-     */
-    private boolean evaluateResults(ISensorVerification verification, StringBuilder sb) {
-        try {
-            verification.verify(mEnvironment, getStats());
-        } catch (AssertionError e) {
-            if (sb.length() > 0) {
-                sb.append(", ");
-            }
-            sb.append(e.getMessage());
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
index b500ea7..9f03f31 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -17,41 +17,42 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.content.Context;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 
 /**
- * An {@link ISensorOperation} which holds a wakelock while performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which holds a wake-lock while performing another
+ * {@link SensorOperation}.
  */
-public class WakeLockOperation extends AbstractSensorOperation {
+public class WakeLockOperation extends SensorOperation {
     private static final String TAG = "WakeLockOperation";
 
-    private final ISensorOperation mOperation;
+    private final SensorOperation mOperation;
     private final Context mContext;
-    private final int mWakelockFlags;
+    private final int mWakeLockFlags;
 
     /**
      * Constructor for {@link WakeLockOperation}.
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param context the context used to access the power manager
-     * @param wakelockFlags the flags used when acquiring the wakelock
+     * @param wakeLockFlags the flags used when acquiring the wake-lock
      */
-    public WakeLockOperation(ISensorOperation operation, Context context, int wakelockFlags) {
+    public WakeLockOperation(SensorOperation operation, Context context, int wakeLockFlags) {
+        super(operation.getStats());
         mOperation = operation;
         mContext = context;
-        mWakelockFlags = wakelockFlags;
+        mWakeLockFlags = wakeLockFlags;
     }
 
     /**
      * Constructor for {@link WakeLockOperation}.
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param context the context used to access the power manager
      */
-    public WakeLockOperation(ISensorOperation operation, Context context) {
+    public WakeLockOperation(SensorOperation operation, Context context) {
         this(operation, context, PowerManager.PARTIAL_WAKE_LOCK);
     }
 
@@ -59,13 +60,12 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        WakeLock wakeLock = pm.newWakeLock(mWakelockFlags, TAG);
-
+        WakeLock wakeLock = pm.newWakeLock(mWakeLockFlags, TAG);
         wakeLock.acquire();
         try {
-            mOperation.execute();
+            mOperation.execute(asTestNode(parent));
         } finally {
             wakeLock.release();
         }
@@ -75,15 +75,7 @@
      * {@inheritDoc}
      */
     @Override
-    public SensorStats getStats() {
-        return mOperation.getStats();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public ISensorOperation clone() {
-        return new WakeLockOperation(mOperation, mContext, mWakelockFlags);
+    public SensorOperation clone() {
+        return new WakeLockOperation(mOperation.clone(), mContext, mWakeLockFlags);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
index 911ae3a..1e775e3 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -18,6 +18,9 @@
 
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.Collection;
+import java.util.List;
+
 /**
  * Abstract class that deals with the synchronization of the sensor verifications.
  */
@@ -27,7 +30,7 @@
      * {@inheritDoc}
      */
     @Override
-    public synchronized void addSensorEvents(TestSensorEvent ... events) {
+    public synchronized void addSensorEvents(Collection<TestSensorEvent> events) {
         for (TestSensorEvent event : events) {
             addSensorEventInternal(event);
         }
@@ -37,14 +40,6 @@
      * {@inheritDoc}
      */
     @Override
-    public synchronized void addSensorEvent(TestSensorEvent event) {
-        addSensorEventInternal(event);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public abstract ISensorVerification clone();
 
     /**
@@ -52,18 +47,38 @@
      */
     protected abstract void addSensorEventInternal(TestSensorEvent event);
 
+    protected <TEvent extends IndexedEvent> int[] getIndexArray(List<TEvent> indexedEvents) {
+        int eventsCount = indexedEvents.size();
+        int[] indices = new int[eventsCount];
+        for (int i = 0; i < eventsCount; i++) {
+            indices[i] = indexedEvents.get(i).index;
+        }
+        return indices;
+    }
+
+    /**
+     * Helper class to store the index and current event.
+     * Events are added to the verification in the order they are generated, the index represents
+     * the position of the given event, in the list of added events.
+     */
+    protected class IndexedEvent {
+        public final int index;
+        public final TestSensorEvent event;
+
+        public IndexedEvent(int index, TestSensorEvent event) {
+            this.index = index;
+            this.event = event;
+        }
+    }
+
     /**
      * Helper class to store the index, previous event, and current event.
      */
-    protected class IndexedEventPair {
-        public final int index;
-        public final TestSensorEvent event;
+    protected class IndexedEventPair extends IndexedEvent {
         public final TestSensorEvent previousEvent;
 
-        public IndexedEventPair(int index, TestSensorEvent event,
-                TestSensorEvent previousEvent) {
-            this.index = index;
-            this.event = event;
+        public IndexedEventPair(int index, TestSensorEvent event, TestSensorEvent previousEvent) {
+            super(index, event);
             this.previousEvent = previousEvent;
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
index 156afa8..b692f0f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -63,19 +63,14 @@
     public void verify(TestSensorEnvironment environment, SensorStats stats) {
         if (environment.isSensorSamplingRateOverloaded()) {
             // the verification is not reliable on environments under load
-            stats.addValue(PASSED_KEY, true);
+            stats.addValue(PASSED_KEY, "skipped (under load)");
             return;
         }
 
         final int count = mEventGaps.size();
         stats.addValue(PASSED_KEY, count == 0);
         stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
-
-        final int[] indices = new int[count];
-        for (int i = 0; i < indices.length; i++) {
-            indices[i] = mEventGaps.get(i).index;
-        }
-        stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, indices);
+        stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, getIndexArray(mEventGaps));
 
         if (count > 0) {
             StringBuilder sb = new StringBuilder();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
index d01e108..6f17e7b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
@@ -22,6 +22,9 @@
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Tests for {@link EventGapVerification}.
  */
@@ -90,11 +93,13 @@
         }
     }
 
-    private ISensorVerification getVerification(int expected, long ... timestamps) {
-        ISensorVerification verification = new EventGapVerification(expected);
+    private static EventGapVerification getVerification(int expected, long ... timestamps) {
+        Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
         for (long timestamp : timestamps) {
-            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+            events.add(new TestSensorEvent(null, timestamp, 0, null));
         }
+        EventGapVerification verification = new EventGapVerification(expected);
+        verification.addSensorEvents(events);
         return verification;
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
index 6598725..d3b317b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
@@ -49,10 +49,8 @@
     @SuppressWarnings("deprecation")
     public static EventOrderingVerification getDefault(TestSensorEnvironment environment) {
         int reportingMode = environment.getSensor().getReportingMode();
-        if (reportingMode != Sensor.REPORTING_MODE_CONTINUOUS
-                && reportingMode != Sensor.REPORTING_MODE_ON_CHANGE) {
+        if (reportingMode == Sensor.REPORTING_MODE_ONE_SHOT)
             return null;
-        }
         return new EventOrderingVerification();
     }
 
@@ -75,12 +73,9 @@
         final int count = mOutOfOrderEvents.size();
         stats.addValue(PASSED_KEY, count == 0);
         stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY, count);
-
-        final int[] indices = new int[count];
-        for (int i = 0; i < indices.length; i++) {
-            indices[i] = mOutOfOrderEvents.get(i).index;
-        }
-        stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY, indices);
+        stats.addValue(
+                SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY,
+                getIndexArray(mOutOfOrderEvents));
 
         if (count > 0) {
             StringBuilder sb = new StringBuilder();
@@ -117,9 +112,9 @@
         if (mPreviousEvent == null) {
             mMaxTimestamp = event.timestamp;
         } else {
-            if (event.timestamp < mMaxTimestamp) {
+            if (event.timestamp <= mMaxTimestamp) {
                 mOutOfOrderEvents.add(new IndexedEventPair(mIndex, event, mPreviousEvent));
-            } else if (event.timestamp > mMaxTimestamp) {
+            } else {
                 mMaxTimestamp = event.timestamp;
             }
         }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
index 88d5c19..b9848fa 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
@@ -22,6 +22,7 @@
 import android.hardware.cts.helpers.TestSensorEvent;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -96,11 +97,13 @@
         assertTrue(indices.contains(4));
     }
 
-    private EventOrderingVerification getVerification(long ... timestamps) {
-        EventOrderingVerification verification = new EventOrderingVerification();
+    private static EventOrderingVerification getVerification(long ... timestamps) {
+        Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
         for (long timestamp : timestamps) {
-            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+            events.add(new TestSensorEvent(null, timestamp, 0, null));
         }
+        EventOrderingVerification verification = new EventOrderingVerification();
+        verification.addSensorEvents(events);
         return verification;
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
new file mode 100644
index 0000000..d4a1f38
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -0,0 +1,153 @@
+/*
+ * 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.cts.helpers.sensorverification;
+
+import junit.framework.Assert;
+
+import android.hardware.SensorEvent;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the timestamp of the {@link SensorEvent} is
+ * synchronized with {@link SystemClock#elapsedRealtimeNanos()}, based on a given threshold.
+ */
+public class EventTimestampSynchronizationVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "timestamp_synchronization_passed";
+
+    // number of indices to print in assertion message before truncating
+    private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+    private static final long DEFAULT_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(500);
+
+    private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<TestSensorEvent>();
+
+    private final long mMaximumSynchronizationErrorNs;
+    private final long mReportLatencyNs;
+
+    /**
+     * Constructs an instance of {@link EventTimestampSynchronizationVerification}.
+     *
+     * @param maximumSynchronizationErrorNs The valid threshold for timestamp synchronization.
+     * @param reportLatencyNs The latency on which batching events are received
+     */
+    public EventTimestampSynchronizationVerification(
+            long maximumSynchronizationErrorNs,
+            long reportLatencyNs) {
+        mMaximumSynchronizationErrorNs = maximumSynchronizationErrorNs;
+        mReportLatencyNs = reportLatencyNs;
+    }
+
+    /**
+     * Gets a default {@link EventTimestampSynchronizationVerification}.
+     *
+     * @param environment The test environment
+     * @return The verification or null if the verification is not supported in the given
+     *         environment.
+     */
+    public static EventTimestampSynchronizationVerification getDefault(
+            TestSensorEnvironment environment) {
+        int reportLatencyUs = environment.getMaxReportLatencyUs();
+        int fifoMaxEventCount = environment.getSensor().getFifoMaxEventCount();
+        if (fifoMaxEventCount > 0) {
+            int fifoBasedReportLatencyUs =
+                    fifoMaxEventCount * environment.getMaximumExpectedSamplingPeriodUs();
+            reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+
+        }
+        long reportLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
+        return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS, reportLatencyNs);
+    }
+
+    @Override
+    public void verify(TestSensorEnvironment environment, SensorStats stats) {
+        StringBuilder errorMessageBuilder =
+                new StringBuilder(" event timestamp synchronization failures: ");
+        List<IndexedEvent> failures = verifyTimestampSynchronization(errorMessageBuilder);
+
+        int failuresCount = failures.size();
+        stats.addValue(SensorStats.EVENT_TIME_SYNCHRONIZATION_COUNT_KEY, failuresCount);
+        stats.addValue(
+                SensorStats.EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY,
+                getIndexArray(failures));
+
+        boolean success = failures.isEmpty();
+        stats.addValue(PASSED_KEY, success);
+        errorMessageBuilder.insert(0, failuresCount);
+        Assert.assertTrue(errorMessageBuilder.toString(), success);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public EventTimestampSynchronizationVerification clone() {
+        return new EventTimestampSynchronizationVerification(
+                mMaximumSynchronizationErrorNs,
+                mReportLatencyNs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        mCollectedEvents.add(event);
+    }
+
+    /**
+     * Verifies timestamp synchronization for all sensor events.
+     * The verification accounts for a lower and upper threshold, such thresholds are adjusted for
+     * batching cases.
+     *
+     * @param builder A string builder to store error messaged found in the collected sensor events.
+     * @return A list of events tha failed the verification.
+     */
+    private List<IndexedEvent> verifyTimestampSynchronization(StringBuilder builder) {
+        int collectedEventsCount = mCollectedEvents.size();
+        ArrayList<IndexedEvent> failures = new ArrayList<IndexedEvent>();
+
+        for (int i = 0; i < collectedEventsCount; ++i) {
+            TestSensorEvent event = mCollectedEvents.get(i);
+            long eventTimestampNs = event.timestamp;
+            long receivedTimestampNs = event.receivedTimestamp;
+            long upperThresholdNs = receivedTimestampNs;
+            long lowerThresholdNs = receivedTimestampNs - mMaximumSynchronizationErrorNs
+                    - mReportLatencyNs;
+
+            if (eventTimestampNs < lowerThresholdNs || eventTimestampNs > upperThresholdNs) {
+                if (failures.size() < TRUNCATE_MESSAGE_LENGTH) {
+                    builder.append("position=").append(i);
+                    builder.append(", timestamp=").append(eventTimestampNs).append("ns");
+                    builder.append(", expected=[").append(lowerThresholdNs);
+                    builder.append(", ").append(upperThresholdNs).append("]ns; ");
+                }
+                failures.add(new IndexedEvent(i, event));
+            }
+        }
+        if (failures.size() >= TRUNCATE_MESSAGE_LENGTH) {
+            builder.append("more; ");
+        }
+        return failures;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
index 24349ce..bbf022a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
@@ -22,6 +22,9 @@
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Tests for {@link EventOrderingVerification}.
  */
@@ -73,15 +76,17 @@
         return new TestSensorEnvironment(getContext(), Sensor.TYPE_ALL, rateUs);
     }
 
-    private ISensorVerification getVerification(
+    private static FrequencyVerification getVerification(
             double lowerThreshold,
             double upperThreshold,
             long ... timestamps) {
-        ISensorVerification verification =
-                new FrequencyVerification(lowerThreshold, upperThreshold);
+        Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
         for (long timestamp : timestamps) {
-            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+            events.add(new TestSensorEvent(null, timestamp, 0, null));
         }
+        FrequencyVerification verification =
+                new FrequencyVerification(lowerThreshold, upperThreshold);
+        verification.addSensorEvents(events);
         return verification;
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
index 4f7b65a..2027f0f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
@@ -20,24 +20,25 @@
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.Collection;
+
 /**
- * Interface describing the sensor verification. This class was designed for to handle streaming
- * events. The methods {@link #addSensorEvent(TestSensorEvent)} and
- * {@link #addSensorEvents(TestSensorEvent...)} should be called in the order that the events are
- * received. The method {@link #verify(TestSensorEnvironment, SensorStats)} should be called after
- * all events are added.
+ * Interface describing the sensor verification.
+ * This class was designed to handle streaming of events.
+ *
+ * The method {@link #addSensorEvents(Collection)} should be called in the order that the events are
+ * received.
+ *
+ * The method {@link #verify(TestSensorEnvironment, SensorStats)} should be called after all events
+ * are added.
  */
 public interface ISensorVerification {
 
     /**
-     * Add a single {@link TestSensorEvent} to be evaluated.
+     * Add a list of {@link TestSensorEvent}s to be evaluated.
      */
-    public void addSensorEvent(TestSensorEvent event);
-
-    /**
-     * Add multiple {@link TestSensorEvent}s to be evaluated.
-     */
-    public void addSensorEvents(TestSensorEvent ... events);
+    // TODO: refactor verifications to be stateless, and pass the list of events in verify()
+    void addSensorEvents(Collection<TestSensorEvent> events);
 
     /**
      * Evaluate all added {@link TestSensorEvent}s and update stats.
@@ -45,10 +46,10 @@
      * @param stats a {@link SensorStats} object used to keep track of the stats.
      * @throws AssertionError if the verification fails.
      */
-    public void verify(TestSensorEnvironment environment, SensorStats stats);
+    void verify(TestSensorEnvironment environment, SensorStats stats);
 
     /**
      * Clones the {@link ISensorVerification}
      */
-    public ISensorVerification clone();
+    ISensorVerification clone();
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
index 0c85b63..50e288c 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
@@ -22,6 +22,8 @@
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -98,11 +100,13 @@
         assertEquals(3.0, jitterValues.get(3));
     }
 
-    private JitterVerification getVerification(int threshold, long ... timestamps) {
-        JitterVerification verification = new JitterVerification(threshold);
+    private static JitterVerification getVerification(int threshold, long ... timestamps) {
+        Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
         for (long timestamp : timestamps) {
-            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+            events.add(new TestSensorEvent(null, timestamp, 0, null));
         }
+        JitterVerification verification = new JitterVerification(threshold);
+        verification.addSensorEvents(events);
         return verification;
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
index bb29330..ac873c1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
@@ -22,6 +22,9 @@
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Tests for {@link MagnitudeVerification}.
  */
@@ -63,12 +66,14 @@
         assertEquals(magnitude, (Float) stats.getValue(SensorStats.MAGNITUDE_KEY), 0.01);
     }
 
-    private MagnitudeVerification getVerification(float expected, float threshold,
+    private static MagnitudeVerification getVerification(float expected, float threshold,
             float[] ... values) {
-        MagnitudeVerification verification = new MagnitudeVerification(expected, threshold);
+        Collection<TestSensorEvent> events = new ArrayList<>(values.length);
         for (float[] value : values) {
-            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+            events.add(new TestSensorEvent(null, 0, 0, value));
         }
+        MagnitudeVerification verification = new MagnitudeVerification(expected, threshold);
+        verification.addSensorEvents(events);
         return verification;
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
index b07ea50..d7fcf9f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
@@ -22,6 +22,9 @@
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Tests for {@link MeanVerification}.
  */
@@ -89,12 +92,14 @@
         verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
     }
 
-    private MeanVerification getVerification(float[] expected, float[] threshold,
+    private static MeanVerification getVerification(float[] expected, float[] threshold,
             float[] ... values) {
-        MeanVerification verification = new MeanVerification(expected, threshold);
+        Collection<TestSensorEvent> events = new ArrayList<>(values.length);
         for (float[] value : values) {
-            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+            events.add(new TestSensorEvent(null, 0, 0, value));
         }
+        MeanVerification verification = new MeanVerification(expected, threshold);
+        verification.addSensorEvents(events);
         return verification;
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
index 5d958f5..617a438 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
@@ -22,6 +22,9 @@
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Tests for {@link StandardDeviationVerification}.
  */
@@ -79,11 +82,15 @@
         }
     }
 
-    private StandardDeviationVerification getVerification(float[] threshold, float[] ... values) {
-        StandardDeviationVerification verification = new StandardDeviationVerification(threshold);
+    private static StandardDeviationVerification getVerification(
+            float[] threshold,
+            float[] ... values) {
+        Collection<TestSensorEvent> events = new ArrayList<>(values.length);
         for (float[] value : values) {
-            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+            events.add(new TestSensorEvent(null, 0, 0, value));
         }
+        StandardDeviationVerification verification = new StandardDeviationVerification(threshold);
+        verification.addSensorEvents(events);
         return verification;
     }
 }
diff --git a/tests/tests/jni/libjnitest/Android.mk b/tests/tests/jni/libjnitest/Android.mk
index d7d1bad..396e161 100644
--- a/tests/tests/jni/libjnitest/Android.mk
+++ b/tests/tests/jni/libjnitest/Android.mk
@@ -29,6 +29,7 @@
 	android_jni_cts_InstanceNonce.c \
 	android_jni_cts_JniCTest.c \
 	android_jni_cts_JniCppTest.cpp \
+	android_jni_cts_JniStaticTest.cpp \
 	android_jni_cts_StaticNonce.c \
 	helper.c \
 	register.c
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp
new file mode 100644
index 0000000..5e8bea0
--- /dev/null
+++ b/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+/*
+ * Native implementation for the JniStaticTest parts.
+ */
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+extern "C" JNIEXPORT jint JNICALL Java_android_jni_cts_ClassLoaderHelper_nativeGetHashCode(
+        JNIEnv* env,
+        jobject obj __attribute__((unused)),
+        jobject appLoader,
+        jclass appLoaderClass) {
+  jmethodID midFindClass = env->GetMethodID(appLoaderClass, "findClass",
+          "(Ljava/lang/String;)Ljava/lang/Class;");
+  jstring coreClsName = env->NewStringUTF("android.jni.cts.ClassLoaderStaticNonce");
+  jobject coreClass = env->CallObjectMethod(appLoader, midFindClass, coreClsName);
+  jmethodID midHashCode = env->GetMethodID((jclass)coreClass, "hashCode", "()I");
+  jint hash = env->CallIntMethod(coreClass, midHashCode);
+
+  return hash;
+}
diff --git a/tests/tests/jni/src/android/jni/cts/ClassLoaderHelper.java b/tests/tests/jni/src/android/jni/cts/ClassLoaderHelper.java
new file mode 100644
index 0000000..c4fe0f0
--- /dev/null
+++ b/tests/tests/jni/src/android/jni/cts/ClassLoaderHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.jni.cts;
+
+class ClassLoaderHelper {
+
+    // Note: To not hava a dependency on other classes, assume that if we do initialize then
+    // it's OK to load the library.
+    static {
+        System.loadLibrary("jnitest");
+    }
+
+    public static boolean run() {
+        ClassLoaderHelper clh = new ClassLoaderHelper();
+
+        int firstHashCode = clh.getHashCodeDirect();
+
+        Runtime.getRuntime().gc();
+
+        int secondHashCode = clh.getHashCodeNative();
+
+        return (firstHashCode == secondHashCode);
+    }
+
+    // Simple helpers to avoid keeping references alive because of dex registers.
+    private int getHashCodeDirect() {
+        return ClassLoaderStaticNonce.class.hashCode();
+    }
+    private int getHashCodeNative() {
+        ClassLoader loader = getClass().getClassLoader();
+        return nativeGetHashCode(loader, loader.getClass());
+    }
+
+    private native int nativeGetHashCode(ClassLoader loader, Class<?> classLoaderClass);
+}
\ No newline at end of file
diff --git a/tests/tests/jni/src/android/jni/cts/ClassLoaderStaticNonce.java b/tests/tests/jni/src/android/jni/cts/ClassLoaderStaticNonce.java
new file mode 100644
index 0000000..d425c77
--- /dev/null
+++ b/tests/tests/jni/src/android/jni/cts/ClassLoaderStaticNonce.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.jni.cts;
+
+import android.util.Log;
+
+/**
+ * Class to help with class-loader check..
+ */
+public class ClassLoaderStaticNonce {
+
+    static Object ctx;
+
+    // Have a static initializer block.
+    static {
+        ctx = new Object();
+        log("Initializing ClassLoaderStaticNonce");
+    }
+
+    private final static A a = new A();
+
+    public static void log(String s) {
+        Log.i("ClassLoaderStaticNone", s);
+    }
+
+    public static class A {
+        // Have a finalizer. This will make the outer class not finalizable when we allocate for
+        // a static field.
+        public void finalize() throws Throwable {
+            super.finalize();
+
+            // Do something so that the finalizer can't be recognized as empty.
+            toNull = null;
+        }
+
+        private Object toNull = new Object();
+    }
+
+
+    public static void setCtx(Object in) {
+        ctx = in;
+    }
+
+    public static Object getCtx() {
+        return ctx;
+    }
+}
diff --git a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
index 3036c71..d4b51b9 100644
--- a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
+++ b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
@@ -24,6 +24,20 @@
  */
 public class JniStaticTest extends JniTestCase {
 
+    static {
+        if (!JniTestCase.isCpuAbiNone()) {
+            System.loadLibrary("jnitest");
+        }
+    }
+
+    /**
+     * Test that accessing classes true JNI works as expected. b/19382130
+     */
+    public void test_classload() {
+        // Use an independent class to do this.
+        assertEquals(true, ClassLoaderHelper.run());
+    }
+
     /**
      * Test a simple no-op and void-returning method call.
      */
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index a985aee..118b8f9 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -49,7 +49,7 @@
  * android.permission.ACCESS_LOCATION_EXTRA_COMMANDS to send extra commands to GPS provider
  */
 public class LocationManagerTest extends InstrumentationTestCase {
-    private static final long TEST_TIME_OUT = 5000;
+    private static final long TEST_TIME_OUT = 10000;
 
     private static final String TEST_MOCK_PROVIDER_NAME = "test_provider";
 
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
index 3765809..b298b97 100644
--- a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -26,6 +26,7 @@
 import android.location.Criteria;
 import android.location.GpsStatus;
 import android.location.GpsStatus.Listener;
+import android.location.GpsStatus.NmeaListener;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
@@ -227,18 +228,32 @@
     @UiThreadTest
     public void testGpsStatusListener() {
         try {
-            // .addGpsStatusListener returns true if the listener added successfully
-            if (mManager.addGpsStatusListener(new MockGpsStatusListener())) {
-                fail("Should have failed to add a gps status listener");
-            }
+            mManager.addGpsStatusListener(new MockGpsStatusListener());
+            fail("Should have failed to add a gps status listener");
         } catch (SecurityException e) {
             // expected
         }
 
         try {
-            if (mManager.addGpsStatusListener(null)) {
-                fail("Should have failed to add null as a gps status listener");
-            }
+            mManager.addGpsStatusListener(null);
+            fail("Should have failed to add null as a gps status listener");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @UiThreadTest
+    public void testGpsStatusNmeaListener() {
+        try {
+            mManager.addNmeaListener(new MockGpsStatusNmeaListener());
+            fail("Should have failed to add a gps status nmea listener");
+        } catch (SecurityException e) {
+            // expected
+        }
+
+        try {
+            mManager.addNmeaListener(null);
+            fail("Should have failed to add null as a gps status nmea listener");
         } catch (SecurityException e) {
             // expected
         }
@@ -478,4 +493,20 @@
             mHasCallOnGpsStatusChanged = true;
         }
     }
+
+    private static class MockGpsStatusNmeaListener implements NmeaListener {
+        private boolean mHasCallOnNmeaReceived;
+
+        public boolean hasCallOnNmeaReceived() {
+            return mHasCallOnNmeaReceived;
+        }
+
+        public void reset(){
+            mHasCallOnNmeaReceived = false;
+        }
+
+        public void onNmeaReceived(long timestamp, String nmea) {
+            mHasCallOnNmeaReceived = true;
+        }
+    }
 }
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 15237a8..77d4bb7 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -16,6 +16,20 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_SRC_FILES := \
+	src/android/media/cts/CodecImage.java \
+	src/android/media/cts/CodecUtils.java
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := ctsmediautil
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
 # and when built explicitly put it in the data partition
@@ -24,7 +38,8 @@
 # include both the 32 and 64 bit versions
 LOCAL_MULTILIB := both
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestserver ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ctsmediautil ctsdeviceutil ctstestserver ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni
 
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index d53b2c6..32df531 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -64,6 +64,11 @@
                 <action android:name="android.intent.action.MAIN"/>
             </intent-filter>
         </service>
+        <service android:name="android.media.cts.StubMediaBrowserService">
+            <intent-filter>
+                <action android:name="android.media.browse.MediaBrowserService" />
+            </intent-filter>
+        </service>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/media/assets/noiseandchirps.ogg b/tests/tests/media/assets/noiseandchirps.ogg
index 5c67a88..1acb643 100644
--- a/tests/tests/media/assets/noiseandchirps.ogg
+++ b/tests/tests/media/assets/noiseandchirps.ogg
Binary files differ
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 2d2033f..23f9f5c 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -20,9 +20,13 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := native-media-jni.cpp
+LOCAL_SRC_FILES := \
+	native-media-jni.cpp \
+	codec-utils-jni.cpp
 
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+LOCAL_C_INCLUDES := \
+	$(JNI_H_INCLUDE) \
+	system/core/include
 
 LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
 
diff --git a/tests/tests/media/libmediandkjni/codec-utils-jni.cpp b/tests/tests/media/libmediandkjni/codec-utils-jni.cpp
new file mode 100644
index 0000000..4f87bb4
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/codec-utils-jni.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Original code copied from NDK Native-media sample code */
+
+//#define LOG_NDEBUG 0
+#define TAG "CodecUtilsJNI"
+#include <log/log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <jni.h>
+
+#include <ScopedLocalRef.h>
+#include <JNIHelp.h>
+
+#include <math.h>
+
+typedef ssize_t offs_t;
+
+struct NativeImage {
+    struct crop {
+        int left;
+        int top;
+        int right;
+        int bottom;
+    } crop;
+    struct plane {
+        const uint8_t *buffer;
+        size_t size;
+        ssize_t colInc;
+        ssize_t rowInc;
+        offs_t cropOffs;
+        size_t cropWidth;
+        size_t cropHeight;
+    } plane[3];
+    int width;
+    int height;
+    int format;
+    long timestamp;
+    size_t numPlanes;
+};
+
+struct ChecksumAlg {
+    virtual void init() = 0;
+    virtual void update(uint8_t c) = 0;
+    virtual uint32_t checksum() = 0;
+    virtual size_t length() = 0;
+protected:
+    virtual ~ChecksumAlg() {}
+};
+
+struct Adler32 : ChecksumAlg {
+    Adler32() {
+        init();
+    }
+    void init() {
+        a = 1;
+        len = b = 0;
+    }
+    void update(uint8_t c) {
+        a += c;
+        b += a;
+        ++len;
+    }
+    uint32_t checksum() {
+        return (a % 65521) + ((b % 65521) << 16);
+    }
+    size_t length() {
+        return len;
+    }
+private:
+    uint32_t a, b;
+    size_t len;
+};
+
+static struct ImageFieldsAndMethods {
+    // android.graphics.ImageFormat
+    int YUV_420_888;
+    // android.media.Image
+    jmethodID methodWidth;
+    jmethodID methodHeight;
+    jmethodID methodFormat;
+    jmethodID methodTimestamp;
+    jmethodID methodPlanes;
+    jmethodID methodCrop;
+    // android.media.Image.Plane
+    jmethodID methodBuffer;
+    jmethodID methodPixelStride;
+    jmethodID methodRowStride;
+    // android.graphics.Rect
+    jfieldID fieldLeft;
+    jfieldID fieldTop;
+    jfieldID fieldRight;
+    jfieldID fieldBottom;
+} gFields;
+static bool gFieldsInitialized = false;
+
+void initializeGlobalFields(JNIEnv *env) {
+    if (gFieldsInitialized) {
+        return;
+    }
+    {   // ImageFormat
+        jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat");
+        const jfieldID fieldYUV420888 = env->GetStaticFieldID(imageFormatClazz, "YUV_420_888", "I");
+        gFields.YUV_420_888 = env->GetStaticIntField(imageFormatClazz, fieldYUV420888);
+        env->DeleteLocalRef(imageFormatClazz);
+        imageFormatClazz = NULL;
+    }
+
+    {   // Image
+        jclass imageClazz = env->FindClass("android/media/cts/CodecImage");
+        gFields.methodWidth  = env->GetMethodID(imageClazz, "getWidth", "()I");
+        gFields.methodHeight = env->GetMethodID(imageClazz, "getHeight", "()I");
+        gFields.methodFormat = env->GetMethodID(imageClazz, "getFormat", "()I");
+        gFields.methodTimestamp = env->GetMethodID(imageClazz, "getTimestamp", "()J");
+        gFields.methodPlanes = env->GetMethodID(
+                imageClazz, "getPlanes", "()[Landroid/media/cts/CodecImage$Plane;");
+        gFields.methodCrop   = env->GetMethodID(
+                imageClazz, "getCropRect", "()Landroid/graphics/Rect;");
+        env->DeleteLocalRef(imageClazz);
+        imageClazz = NULL;
+    }
+
+    {   // Image.Plane
+        jclass planeClazz = env->FindClass("android/media/cts/CodecImage$Plane");
+        gFields.methodBuffer = env->GetMethodID(planeClazz, "getBuffer", "()Ljava/nio/ByteBuffer;");
+        gFields.methodPixelStride = env->GetMethodID(planeClazz, "getPixelStride", "()I");
+        gFields.methodRowStride = env->GetMethodID(planeClazz, "getRowStride", "()I");
+        env->DeleteLocalRef(planeClazz);
+        planeClazz = NULL;
+    }
+
+    {   // Rect
+        jclass rectClazz = env->FindClass("android/graphics/Rect");
+        gFields.fieldLeft   = env->GetFieldID(rectClazz, "left", "I");
+        gFields.fieldTop    = env->GetFieldID(rectClazz, "top", "I");
+        gFields.fieldRight  = env->GetFieldID(rectClazz, "right", "I");
+        gFields.fieldBottom = env->GetFieldID(rectClazz, "bottom", "I");
+        env->DeleteLocalRef(rectClazz);
+        rectClazz = NULL;
+    }
+    gFieldsInitialized = true;
+}
+
+NativeImage *getNativeImage(JNIEnv *env, jobject image, jobject area = NULL) {
+    if (image == NULL) {
+        jniThrowNullPointerException(env, "image is null");
+        return NULL;
+    }
+
+    initializeGlobalFields(env);
+
+    NativeImage *img = new NativeImage;
+    img->format = env->CallIntMethod(image, gFields.methodFormat);
+    img->width  = env->CallIntMethod(image, gFields.methodWidth);
+    img->height = env->CallIntMethod(image, gFields.methodHeight);
+    img->timestamp = env->CallLongMethod(image, gFields.methodTimestamp);
+
+    jobject cropRect = NULL;
+    if (area == NULL) {
+        cropRect = env->CallObjectMethod(image, gFields.methodCrop);
+        area = cropRect;
+    }
+
+    img->crop.left   = env->GetIntField(area, gFields.fieldLeft);
+    img->crop.top    = env->GetIntField(area, gFields.fieldTop);
+    img->crop.right  = env->GetIntField(area, gFields.fieldRight);
+    img->crop.bottom = env->GetIntField(area, gFields.fieldBottom);
+    if (img->crop.right == 0 && img->crop.bottom == 0) {
+        img->crop.right  = img->width;
+        img->crop.bottom = img->height;
+    }
+
+    if (cropRect != NULL) {
+        env->DeleteLocalRef(cropRect);
+        cropRect = NULL;
+    }
+
+    if (img->format != gFields.YUV_420_888) {
+        jniThrowException(
+                env, "java/lang/UnsupportedOperationException",
+                "only support YUV_420_888 images");
+        delete img;
+        img = NULL;
+        return NULL;
+    }
+    img->numPlanes = 3;
+
+    ScopedLocalRef<jobjectArray> planesArray(
+            env, (jobjectArray)env->CallObjectMethod(image, gFields.methodPlanes));
+    int xDecim = 0;
+    int yDecim = 0;
+    for (size_t ix = 0; ix < img->numPlanes; ++ix) {
+        ScopedLocalRef<jobject> plane(
+                env, env->GetObjectArrayElement(planesArray.get(), (jsize)ix));
+        img->plane[ix].colInc = env->CallIntMethod(plane.get(), gFields.methodPixelStride);
+        img->plane[ix].rowInc = env->CallIntMethod(plane.get(), gFields.methodRowStride);
+        ScopedLocalRef<jobject> buffer(
+                env, env->CallObjectMethod(plane.get(), gFields.methodBuffer));
+
+        img->plane[ix].buffer = (const uint8_t *)env->GetDirectBufferAddress(buffer.get());
+        img->plane[ix].size = env->GetDirectBufferCapacity(buffer.get());
+
+        img->plane[ix].cropOffs =
+            (img->crop.left >> xDecim) * img->plane[ix].colInc
+                    + (img->crop.top >> yDecim) * img->plane[ix].rowInc;
+        img->plane[ix].cropHeight =
+            ((img->crop.bottom + (1 << yDecim) - 1) >> yDecim) - (img->crop.top >> yDecim);
+        img->plane[ix].cropWidth =
+            ((img->crop.right + (1 << xDecim) - 1) >> xDecim) - (img->crop.left >> xDecim);
+
+        // sanity check on increments
+        ssize_t widthOffs =
+            (((img->width + (1 << xDecim) - 1) >> xDecim) - 1) * img->plane[ix].colInc;
+        ssize_t heightOffs =
+            (((img->height + (1 << yDecim) - 1) >> yDecim) - 1) * img->plane[ix].rowInc;
+        if (widthOffs < 0 || heightOffs < 0
+                || widthOffs + heightOffs >= (ssize_t)img->plane[ix].size) {
+            jniThrowException(
+                    env, "java/lang/IndexOutOfBoundsException", "plane exceeds bytearray");
+            delete img;
+            img = NULL;
+            return NULL;
+        }
+        xDecim = yDecim = 1;
+    }
+    return img;
+}
+
+extern "C" jint Java_android_media_cts_CodecUtils_getImageChecksum(JNIEnv *env,
+        jclass /*clazz*/, jobject image)
+{
+    NativeImage *img = getNativeImage(env, image);
+    if (img == NULL) {
+        return 0;
+    }
+
+    Adler32 adler;
+    for (size_t ix = 0; ix < img->numPlanes; ++ix) {
+        const uint8_t *row = img->plane[ix].buffer + img->plane[ix].cropOffs;
+        for (size_t y = img->plane[ix].cropHeight; y > 0; --y) {
+            const uint8_t *col = row;
+            ssize_t colInc = img->plane[ix].colInc;
+            for (size_t x = img->plane[ix].cropWidth; x > 0; --x) {
+                adler.update(*col);
+                col += colInc;
+            }
+            row += img->plane[ix].rowInc;
+        }
+    }
+    ALOGV("adler %zu/%u", adler.length(), adler.checksum());
+    return adler.checksum();
+}
+
+/* tiled copy that loops around source image boundary */
+extern "C" void Java_android_media_cts_CodecUtils_copyFlexYUVImage(JNIEnv *env,
+        jclass /*clazz*/, jobject target, jobject source)
+{
+    NativeImage *tgt = getNativeImage(env, target);
+    NativeImage *src = getNativeImage(env, source);
+    if (tgt != NULL && src != NULL) {
+        ALOGV("copyFlexYUVImage %dx%d (%d,%d..%d,%d) (%zux%zu) %+zd%+zd %+zd%+zd %+zd%+zd <= "
+                "%dx%d (%d, %d..%d, %d) (%zux%zu) %+zd%+zd %+zd%+zd %+zd%+zd",
+                tgt->width, tgt->height,
+                tgt->crop.left, tgt->crop.top, tgt->crop.right, tgt->crop.bottom,
+                tgt->plane[0].cropWidth, tgt->plane[0].cropHeight,
+                tgt->plane[0].rowInc, tgt->plane[0].colInc,
+                tgt->plane[1].rowInc, tgt->plane[1].colInc,
+                tgt->plane[2].rowInc, tgt->plane[2].colInc,
+                src->width, src->height,
+                src->crop.left, src->crop.top, src->crop.right, src->crop.bottom,
+                src->plane[0].cropWidth, src->plane[0].cropHeight,
+                src->plane[0].rowInc, src->plane[0].colInc,
+                src->plane[1].rowInc, src->plane[1].colInc,
+                src->plane[2].rowInc, src->plane[2].colInc);
+        for (size_t ix = 0; ix < tgt->numPlanes; ++ix) {
+            uint8_t *row = const_cast<uint8_t *>(tgt->plane[ix].buffer) + tgt->plane[ix].cropOffs;
+            for (size_t y = 0; y < tgt->plane[ix].cropHeight; ++y) {
+                uint8_t *col = row;
+                ssize_t colInc = tgt->plane[ix].colInc;
+                const uint8_t *srcRow = (src->plane[ix].buffer + src->plane[ix].cropOffs
+                        + src->plane[ix].rowInc * (y % src->plane[ix].cropHeight));
+                for (size_t x = 0; x < tgt->plane[ix].cropWidth; ++x) {
+                    *col = srcRow[src->plane[ix].colInc * (x % src->plane[ix].cropWidth)];
+                    col += colInc;
+                }
+                row += tgt->plane[ix].rowInc;
+            }
+        }
+    }
+}
+
+extern "C" void Java_android_media_cts_CodecUtils_fillImageRectWithYUV(JNIEnv *env,
+        jclass /*clazz*/, jobject image, jobject area, jint y, jint u, jint v)
+{
+    NativeImage *img = getNativeImage(env, image, area);
+    if (img == NULL) {
+        return;
+    }
+
+    for (size_t ix = 0; ix < img->numPlanes; ++ix) {
+        const uint8_t *row = img->plane[ix].buffer + img->plane[ix].cropOffs;
+        uint8_t val = ix == 0 ? y : ix == 1 ? u : v;
+        for (size_t y = img->plane[ix].cropHeight; y > 0; --y) {
+            uint8_t *col = (uint8_t *)row;
+            ssize_t colInc = img->plane[ix].colInc;
+            for (size_t x = img->plane[ix].cropWidth; x > 0; --x) {
+                *col = val;
+                col += colInc;
+            }
+            row += img->plane[ix].rowInc;
+        }
+    }
+}
+
+void getRawStats(NativeImage *img, jlong rawStats[10])
+{
+    // this works best if crop area is even
+
+    uint64_t sum_x[3]  = { 0, 0, 0 }; // Y, U, V
+    uint64_t sum_xx[3] = { 0, 0, 0 }; // YY, UU, VV
+    uint64_t sum_xy[3] = { 0, 0, 0 }; // YU, YV, UV
+
+    const uint8_t *yrow = img->plane[0].buffer + img->plane[0].cropOffs;
+    const uint8_t *urow = img->plane[1].buffer + img->plane[1].cropOffs;
+    const uint8_t *vrow = img->plane[2].buffer + img->plane[2].cropOffs;
+
+    ssize_t ycolInc = img->plane[0].colInc;
+    ssize_t ucolInc = img->plane[1].colInc;
+    ssize_t vcolInc = img->plane[2].colInc;
+
+    ssize_t yrowInc = img->plane[0].rowInc;
+    ssize_t urowInc = img->plane[1].rowInc;
+    ssize_t vrowInc = img->plane[2].rowInc;
+
+    size_t rightOdd = img->crop.right & 1;
+    size_t bottomOdd = img->crop.bottom & 1;
+
+    for (size_t y = img->plane[0].cropHeight; y; --y) {
+        uint8_t *ycol = (uint8_t *)yrow;
+        uint8_t *ucol = (uint8_t *)urow;
+        uint8_t *vcol = (uint8_t *)vrow;
+
+        for (size_t x = img->plane[0].cropWidth; x; --x) {
+            uint64_t Y = *ycol;
+            uint64_t U = *ucol;
+            uint64_t V = *vcol;
+
+            sum_x[0] += Y;
+            sum_x[1] += U;
+            sum_x[2] += V;
+            sum_xx[0] += Y * Y;
+            sum_xx[1] += U * U;
+            sum_xx[2] += V * V;
+            sum_xy[0] += Y * U;
+            sum_xy[1] += Y * V;
+            sum_xy[2] += U * V;
+
+            ycol += ycolInc;
+            if (rightOdd ^ (x & 1)) {
+                ucol += ucolInc;
+                vcol += vcolInc;
+            }
+        }
+
+        yrow += yrowInc;
+        if (bottomOdd ^ (y & 1)) {
+            urow += urowInc;
+            vrow += vrowInc;
+        }
+    }
+
+    rawStats[0] = img->plane[0].cropWidth * (uint64_t)img->plane[0].cropHeight;
+    for (size_t i = 0; i < 3; i++) {
+        rawStats[i + 1] = sum_x[i];
+        rawStats[i + 4] = sum_xx[i];
+        rawStats[i + 7] = sum_xy[i];
+    }
+}
+
+bool Raw2YUVStats(jlong rawStats[10], jfloat stats[9]) {
+    int64_t sum_x[3], sum_xx[3]; // Y, U, V
+    int64_t sum_xy[3];           // YU, YV, UV
+
+    int64_t num = rawStats[0];   // #Y,U,V
+    for (size_t i = 0; i < 3; i++) {
+        sum_x[i] = rawStats[i + 1];
+        sum_xx[i] = rawStats[i + 4];
+        sum_xy[i] = rawStats[i + 7];
+    }
+
+    if (num > 0) {
+        stats[0] = sum_x[0] / (float)num;  // y average
+        stats[1] = sum_x[1] / (float)num;  // u average
+        stats[2] = sum_x[2] / (float)num;  // v average
+
+        // 60 bits for 4Mpixel image
+        // adding 1 to avoid degenerate case when deviation is 0
+        stats[3] = sqrtf((sum_xx[0] + 1) * num - sum_x[0] * sum_x[0]) / num; // y stdev
+        stats[4] = sqrtf((sum_xx[1] + 1) * num - sum_x[1] * sum_x[1]) / num; // u stdev
+        stats[5] = sqrtf((sum_xx[2] + 1) * num - sum_x[2] * sum_x[2]) / num; // v stdev
+
+        // yu covar
+        stats[6] = (float)(sum_xy[0] + 1 - sum_x[0] * sum_x[1] / num) / num / stats[3] / stats[4];
+        // yv covar
+        stats[7] = (float)(sum_xy[1] + 1 - sum_x[0] * sum_x[2] / num) / num / stats[3] / stats[5];
+        // uv covar
+        stats[8] = (float)(sum_xy[2] + 1 - sum_x[1] * sum_x[2] / num) / num / stats[4] / stats[5];
+        return true;
+    } else {
+        return false;
+    }
+}
+
+extern "C" jobject Java_android_media_cts_CodecUtils_getRawStats(JNIEnv *env,
+        jclass /*clazz*/, jobject image, jobject area)
+{
+    NativeImage *img = getNativeImage(env, image, area);
+    if (img == NULL) {
+        return NULL;
+    }
+
+    jlong rawStats[10];
+    getRawStats(img, rawStats);
+    jlongArray jstats = env->NewLongArray(10);
+    if (jstats != NULL) {
+        env->SetLongArrayRegion(jstats, 0, 10, rawStats);
+    }
+    return jstats;
+}
+
+extern "C" jobject Java_android_media_cts_CodecUtils_getYUVStats(JNIEnv *env,
+        jclass /*clazz*/, jobject image, jobject area)
+{
+    NativeImage *img = getNativeImage(env, image, area);
+    if (img == NULL) {
+        return NULL;
+    }
+
+    jlong rawStats[10];
+    getRawStats(img, rawStats);
+    jfloat stats[9];
+    jfloatArray jstats = NULL;
+    if (Raw2YUVStats(rawStats, stats)) {
+        jstats = env->NewFloatArray(9);
+        if (jstats != NULL) {
+            env->SetFloatArrayRegion(jstats, 0, 9, stats);
+        }
+    } else {
+        jniThrowRuntimeException(env, "empty area");
+    }
+
+    return jstats;
+}
+
+extern "C" jobject Java_android_media_cts_CodecUtils_Raw2YUVStats(JNIEnv *env,
+        jclass /*clazz*/, jobject jrawStats)
+{
+    jfloatArray jstats = NULL;
+    jlong rawStats[10];
+    env->GetLongArrayRegion((jlongArray)jrawStats, 0, 10, rawStats);
+    if (!env->ExceptionCheck()) {
+        jfloat stats[9];
+        if (Raw2YUVStats(rawStats, stats)) {
+            jstats = env->NewFloatArray(9);
+            if (jstats != NULL) {
+                env->SetFloatArrayRegion(jstats, 0, 9, stats);
+            }
+        } else {
+            jniThrowRuntimeException(env, "no raw statistics");
+        }
+    }
+    return jstats;
+}
diff --git a/tests/tests/media/libmediandkjni/native-media-jni.cpp b/tests/tests/media/libmediandkjni/native-media-jni.cpp
index 850932f1..9bca242 100644
--- a/tests/tests/media/libmediandkjni/native-media-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-media-jni.cpp
@@ -16,7 +16,10 @@
 
 /* Original code copied from NDK Native-media sample code */
 
-#undef NDEBUG
+//#define LOG_NDEBUG 0
+#define TAG "NativeMedia"
+#include <log/log.h>
+
 #include <assert.h>
 #include <jni.h>
 #include <pthread.h>
@@ -27,13 +30,6 @@
 
 #include <android/native_window_jni.h>
 
-// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
-#include <android/log.h>
-#define TAG "NativeMedia"
-#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
-#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
-#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
-
 #include "ndk/NdkMediaExtractor.h"
 #include "ndk/NdkMediaCodec.h"
 #include "ndk/NdkMediaCrypto.h"
diff --git a/tests/tests/media/res/raw/sine1khzm40db.wav b/tests/tests/media/res/raw/sine1khzm40db.wav
new file mode 100644
index 0000000..ba541c4
--- /dev/null
+++ b/tests/tests/media/res/raw/sine1khzm40db.wav
Binary files differ
diff --git a/tests/tests/media/res/raw/sine1khzs40dblong.mp3 b/tests/tests/media/res/raw/sine1khzs40dblong.mp3
new file mode 100644
index 0000000..29bc683
--- /dev/null
+++ b/tests/tests/media/res/raw/sine1khzs40dblong.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_h264.mp4 b/tests/tests/media/res/raw/swirl_128x128_h264.mp4
new file mode 100644
index 0000000..3ff485a
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_h265.mp4 b/tests/tests/media/res/raw/swirl_128x128_h265.mp4
new file mode 100644
index 0000000..a0b112b
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_128x128_mpeg4.mp4
new file mode 100644
index 0000000..694ce95
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_vp8.webm b/tests/tests/media/res/raw/swirl_128x128_vp8.webm
new file mode 100644
index 0000000..7b606a2
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_vp9.webm b/tests/tests/media/res/raw/swirl_128x128_vp9.webm
new file mode 100644
index 0000000..7acff11
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x96_h263.3gp b/tests/tests/media/res/raw/swirl_128x96_h263.3gp
new file mode 100644
index 0000000..f0ef242
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x96_h263.3gp
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_h264.mp4 b/tests/tests/media/res/raw/swirl_130x132_h264.mp4
new file mode 100644
index 0000000..60027fd
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_h265.mp4 b/tests/tests/media/res/raw/swirl_130x132_h265.mp4
new file mode 100644
index 0000000..46fab26
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_130x132_mpeg4.mp4
new file mode 100644
index 0000000..ed6b529
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_vp8.webm b/tests/tests/media/res/raw/swirl_130x132_vp8.webm
new file mode 100644
index 0000000..a3f2d21
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_vp9.webm b/tests/tests/media/res/raw/swirl_130x132_vp9.webm
new file mode 100644
index 0000000..840dc59
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_h264.mp4 b/tests/tests/media/res/raw/swirl_132x130_h264.mp4
new file mode 100644
index 0000000..dc17f8f
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_h265.mp4 b/tests/tests/media/res/raw/swirl_132x130_h265.mp4
new file mode 100644
index 0000000..f9a59f5
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_132x130_mpeg4.mp4
new file mode 100644
index 0000000..ed975db
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_vp8.webm b/tests/tests/media/res/raw/swirl_132x130_vp8.webm
new file mode 100644
index 0000000..8cd8d4e
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_vp9.webm b/tests/tests/media/res/raw/swirl_132x130_vp9.webm
new file mode 100644
index 0000000..4a8d79f
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_h264.mp4 b/tests/tests/media/res/raw/swirl_136x144_h264.mp4
new file mode 100644
index 0000000..bc5fadf
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_h265.mp4 b/tests/tests/media/res/raw/swirl_136x144_h265.mp4
new file mode 100644
index 0000000..38f1fc8
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_136x144_mpeg4.mp4
new file mode 100644
index 0000000..c74bd96
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_vp8.webm b/tests/tests/media/res/raw/swirl_136x144_vp8.webm
new file mode 100644
index 0000000..960d02f
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_vp9.webm b/tests/tests/media/res/raw/swirl_136x144_vp9.webm
new file mode 100644
index 0000000..5898f07
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_h264.mp4 b/tests/tests/media/res/raw/swirl_144x136_h264.mp4
new file mode 100644
index 0000000..962a218
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_h265.mp4 b/tests/tests/media/res/raw/swirl_144x136_h265.mp4
new file mode 100644
index 0000000..8098621
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_144x136_mpeg4.mp4
new file mode 100644
index 0000000..81c1db3
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_vp8.webm b/tests/tests/media/res/raw/swirl_144x136_vp8.webm
new file mode 100644
index 0000000..b050ade
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_vp9.webm b/tests/tests/media/res/raw/swirl_144x136_vp9.webm
new file mode 100644
index 0000000..9c0539a
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_176x144_h263.3gp b/tests/tests/media/res/raw/swirl_176x144_h263.3gp
new file mode 100644
index 0000000..ee51660
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_176x144_h263.3gp
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_352x288_h263.3gp b/tests/tests/media/res/raw/swirl_352x288_h263.3gp
new file mode 100644
index 0000000..53830a9
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_352x288_h263.3gp
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
new file mode 100644
index 0000000..c8b9512
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
deleted file mode 100644
index b0cd94d..0000000
--- a/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
similarity index 91%
rename from tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
rename to tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
index 2e769e6..a5eddd3 100644
--- a/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
+++ b/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz.webm
new file mode 100644
index 0000000..d6f0f3b
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
deleted file mode 100644
index 10a1a5d..0000000
--- a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
similarity index 76%
rename from tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
rename to tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
index b060ca6..15c34b9 100644
--- a/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
+++ b/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_h264_871kbps_30fps.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_h264_871kbps_30fps.mp4
new file mode 100644
index 0000000..55a83e7
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_h264_871kbps_30fps.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index e6c4d8a..969963d 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -90,7 +90,7 @@
                 MediaFormat.MIMETYPE_VIDEO_VP8,
                 "OMX.google.vp8.decoder",
                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
-                R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz);
+                R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
     }
 
     public Iterable<Codec> VP9(CodecFactory factory) {
@@ -447,7 +447,8 @@
                             this, c, mediaIx) {
                         public void run() {
                             try {
-                                mDecoder.configureAndStart(stepFormat(), stepSurface());
+                                //mDecoder.configureAndStart(stepFormat(), stepSurface());
+                                if(mDecoder.configureAndStart(stepFormat(), stepSurface())){
                                 int decodedFrames = -mDecoder.queueInputBufferRange(
                                         stepMedia(),
                                         0 /* startFrame */,
@@ -461,6 +462,7 @@
                                 warn(mDecoder.getWarnings());
                                 mDecoder.clearWarnings();
                                 mDecoder.flush();
+	                            }
                             } finally {
                                 mDecoder.stop();
                             }
@@ -856,10 +858,16 @@
             mWarnings.clear();
         }
 
-        public void configureAndStart(MediaFormat format, TestSurface surface) {
+        public boolean configureAndStart(MediaFormat format, TestSurface surface) {
             mSurface = surface;
             Log.i(TAG, "configure(" + format + ", " + mSurface.getSurface() + ")");
-            mCodec.configure(format, mSurface.getSurface(), null /* crypto */, 0 /* flags */);
+            try{
+			mCodec.configure(format, mSurface.getSurface(), null /* crypto */, 0 /* flags */);
+			}
+			catch (Exception e){
+				Log.i(TAG, "Unsupported Codec");
+				return false;
+			}
             Log.i(TAG, "start");
             mCodec.start();
             mInputBuffers = mCodec.getInputBuffers();
@@ -869,6 +877,7 @@
                   mOutputBuffers.length + "output[" +
                   (mOutputBuffers[0] == null ? null : mOutputBuffers[0].capacity()) + "]");
             mQueuedEos = false;
+            return true;
         }
 
         public void stop() {
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index a342c37..be68756 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1310,36 +1310,163 @@
     }
 
     public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate,
-            final double frequency) {
+            final double frequency, double sweep) {
         final double rad = 2 * Math.PI * frequency / sampleRate;
         byte[] vai = new byte[bufferSamples];
+        sweep = Math.PI * sweep / ((double)sampleRate * vai.length);
         for (int j = 0; j < vai.length; j++) {
-            int unsigned =  (int)(Math.sin(j * rad) * Byte.MAX_VALUE) + Byte.MAX_VALUE & 0xFF;
+            int unsigned =  (int)(Math.sin(j * (rad + j * sweep)) * Byte.MAX_VALUE)
+                    + Byte.MAX_VALUE & 0xFF;
             vai[j] = (byte) unsigned;
         }
         return vai;
     }
 
     public static short[] createSoundDataInShortArray(int bufferSamples, final int sampleRate,
-            final double frequency) {
+            final double frequency, double sweep) {
         final double rad = 2 * Math.PI * frequency / sampleRate;
         short[] vai = new short[bufferSamples];
+        sweep = Math.PI * sweep / ((double)sampleRate * vai.length);
         for (int j = 0; j < vai.length; j++) {
-            vai[j] = (short)(Math.sin(j * rad) * Short.MAX_VALUE);
+            vai[j] = (short)(Math.sin(j * (rad + j * sweep)) * Short.MAX_VALUE);
         }
         return vai;
     }
 
     public static float[] createSoundDataInFloatArray(int bufferSamples, final int sampleRate,
-            final double frequency) {
+            final double frequency, double sweep) {
         final double rad = 2 * Math.PI * frequency / sampleRate;
         float[] vaf = new float[bufferSamples];
+        sweep = Math.PI * sweep / ((double)sampleRate * vaf.length);
         for (int j = 0; j < vaf.length; j++) {
-            vaf[j] = (float)(Math.sin(j * rad));
+            vaf[j] = (float)(Math.sin(j * (rad + j * sweep)));
         }
         return vaf;
     }
 
+    public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate,
+            final double frequency) {
+        return createSoundDataInByteArray(bufferSamples, sampleRate, frequency, 0 /*sweep*/);
+    }
+
+    public static short[] createSoundDataInShortArray(int bufferSamples, final int sampleRate,
+            final double frequency) {
+        return createSoundDataInShortArray(bufferSamples, sampleRate, frequency, 0 /*sweep*/);
+    }
+
+    public static float[] createSoundDataInFloatArray(int bufferSamples, final int sampleRate,
+            final double frequency) {
+        return createSoundDataInFloatArray(bufferSamples, sampleRate, frequency, 0 /*sweep*/);
+    }
+
+    public void testPlayStaticData() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        // constants for test
+        final String TEST_NAME = "testPlayStaticData";
+        final int TEST_FORMAT_ARRAY[] = {  // 6 chirps repeated (TEST_LOOPS+1) times, 3 times
+                AudioFormat.ENCODING_PCM_8BIT,
+                AudioFormat.ENCODING_PCM_16BIT,
+                AudioFormat.ENCODING_PCM_FLOAT,
+        };
+        final int TEST_SR_ARRAY[] = {
+                12055, // Note multichannel tracks will sound very short at low sample rates
+                48000,
+        };
+        final int TEST_CONF_ARRAY[] = {
+                AudioFormat.CHANNEL_OUT_MONO,    // 1.0
+                AudioFormat.CHANNEL_OUT_STEREO,  // 2.0
+                AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, // 7.1
+        };
+        final int TEST_MODE = AudioTrack.MODE_STATIC;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+        final double TEST_SWEEP = 100;
+        final int TEST_LOOPS = 1;
+
+        for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+            double frequency = 400; // frequency changes for each test
+            for (int TEST_SR : TEST_SR_ARRAY) {
+                for (int TEST_CONF : TEST_CONF_ARRAY) {
+                    // -------- initialization --------------
+                    final int seconds = 1;
+                    final int channelCount = Integer.bitCount(TEST_CONF);
+                    final int bufferFrames = seconds * TEST_SR;
+                    final int bufferSamples = bufferFrames * channelCount;
+                    final int bufferSize = bufferSamples
+                            * AudioFormat.getBytesPerSample(TEST_FORMAT);
+                    final double testFrequency = frequency / channelCount;
+                    final long MILLISECONDS_PER_SECOND = 1000;
+                    AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
+                            TEST_CONF, TEST_FORMAT, bufferSize, TEST_MODE);
+                    assertEquals(TEST_NAME, track.getState(), AudioTrack.STATE_NO_STATIC_DATA);
+
+                    // -------- test --------------
+
+                    // test setLoopPoints and setPosition can be called here.
+                    assertEquals(TEST_NAME,
+                            track.setPlaybackHeadPosition(bufferFrames/2),
+                            android.media.AudioTrack.SUCCESS);
+                    assertEquals(TEST_NAME,
+                            track.setLoopPoints(
+                                    0 /*startInFrames*/, bufferFrames, 10 /*loopCount*/),
+                            android.media.AudioTrack.SUCCESS);
+                    // only need to write once to the static track
+                    switch (TEST_FORMAT) {
+                    case AudioFormat.ENCODING_PCM_8BIT: {
+                        byte data[] = createSoundDataInByteArray(
+                                bufferSamples, TEST_SR,
+                                testFrequency, TEST_SWEEP);
+                        assertEquals(TEST_NAME,
+                                track.write(data, 0 /*offsetInBytes*/, data.length),
+                                bufferSamples);
+                        } break;
+                    case AudioFormat.ENCODING_PCM_16BIT: {
+                        short data[] = createSoundDataInShortArray(
+                                bufferSamples, TEST_SR,
+                                testFrequency, TEST_SWEEP);
+                        assertEquals(TEST_NAME,
+                                track.write(data, 0 /*offsetInBytes*/, data.length),
+                                bufferSamples);
+                        } break;
+                    case AudioFormat.ENCODING_PCM_FLOAT: {
+                        float data[] = createSoundDataInFloatArray(
+                                bufferSamples, TEST_SR,
+                                testFrequency, TEST_SWEEP);
+                        assertEquals(TEST_NAME,
+                                track.write(data, 0 /*offsetInBytes*/, data.length,
+                                        AudioTrack.WRITE_BLOCKING),
+                                bufferSamples);
+                        } break;
+                    }
+                    assertEquals(TEST_NAME, track.getState(), AudioTrack.STATE_INITIALIZED);
+                    // test setLoopPoints and setPosition can be called here.
+                    assertEquals(TEST_NAME,
+                            track.setPlaybackHeadPosition(0 /*positionInFrames*/),
+                            android.media.AudioTrack.SUCCESS);
+                    assertEquals(TEST_NAME,
+                            track.setLoopPoints(0 /*startInFrames*/, bufferFrames, TEST_LOOPS),
+                            android.media.AudioTrack.SUCCESS);
+
+                    track.play();
+                    Thread.sleep(seconds * MILLISECONDS_PER_SECOND * (TEST_LOOPS + 1));
+                    Thread.sleep(WAIT_MSEC);
+
+                    // Check position after looping. AudioTrack.getPlaybackHeadPosition() returns
+                    // the running count of frames played, not the actual static buffer position.
+                    int position = track.getPlaybackHeadPosition();
+                    assertEquals(TEST_NAME, position, bufferFrames * (TEST_LOOPS + 1));
+
+                    track.stop();
+                    Thread.sleep(WAIT_MSEC);
+                    // -------- tear down --------------
+                    track.release();
+                    frequency += 70; // increment test tone frequency
+                }
+            }
+        }
+    }
+
     public void testPlayStreamData() throws Exception {
         // constants for test
         final String TEST_NAME = "testPlayStreamData";
diff --git a/tests/tests/media/src/android/media/cts/CodecImage.java b/tests/tests/media/src/android/media/cts/CodecImage.java
new file mode 100644
index 0000000..60a644a
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/CodecImage.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import java.nio.ByteBuffer;
+import java.lang.AutoCloseable;
+
+import android.graphics.Rect;
+
+/**
+ * <p>A single complete image buffer to use with a media source such as a
+ * {@link MediaCodec} or a
+ * {@link android.hardware.camera2.CameraDevice CameraDevice}.</p>
+ *
+ * <p>This class allows for efficient direct application access to the pixel
+ * data of the CodecImage through one or more
+ * {@link java.nio.ByteBuffer ByteBuffers}. Each buffer is encapsulated in a
+ * {@link Plane} that describes the layout of the pixel data in that plane. Due
+ * to this direct access, and unlike the {@link android.graphics.Bitmap Bitmap} class,
+ * Images are not directly usable as UI resources.</p>
+ *
+ * <p>Since Images are often directly produced or consumed by hardware
+ * components, they are a limited resource shared across the system, and should
+ * be closed as soon as they are no longer needed.</p>
+ *
+ * <p>For example, when using the {@link ImageReader} class to read out Images
+ * from various media sources, not closing old CodecImage objects will prevent the
+ * availability of new Images once
+ * {@link ImageReader#getMaxImages the maximum outstanding image count} is
+ * reached. When this happens, the function acquiring new Images will typically
+ * throw an {@link IllegalStateException}.</p>
+ *
+ * @see ImageReader
+ */
+public abstract class CodecImage implements AutoCloseable {
+    /**
+     * Get the format for this image. This format determines the number of
+     * ByteBuffers needed to represent the image, and the general layout of the
+     * pixel data in each in ByteBuffer.
+     *
+     * <p>
+     * The format is one of the values from
+     * {@link android.graphics.ImageFormat ImageFormat}. The mapping between the
+     * formats and the planes is as follows:
+     * </p>
+     *
+     * <table>
+     * <tr>
+     *   <th>Format</th>
+     *   <th>Plane count</th>
+     *   <th>Layout details</th>
+     * </tr>
+     * <tr>
+     *   <td>{@link android.graphics.ImageFormat#JPEG JPEG}</td>
+     *   <td>1</td>
+     *   <td>Compressed data, so row and pixel strides are 0. To uncompress, use
+     *      {@link android.graphics.BitmapFactory#decodeByteArray BitmapFactory#decodeByteArray}.
+     *   </td>
+     * </tr>
+     * <tr>
+     *   <td>{@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}</td>
+     *   <td>3</td>
+     *   <td>A luminance plane followed by the Cb and Cr chroma planes.
+     *     The chroma planes have half the width and height of the luminance
+     *     plane (4:2:0 subsampling). Each pixel sample in each plane has 8 bits.
+     *     Each plane has its own row stride and pixel stride.</td>
+     * </tr>
+     * <tr>
+     *   <td>{@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}</td>
+     *   <td>1</td>
+     *   <td>A single plane of raw sensor image data, with 16 bits per color
+     *     sample. The details of the layout need to be queried from the source of
+     *     the raw sensor data, such as
+     *     {@link android.hardware.camera2.CameraDevice CameraDevice}.
+     *   </td>
+     * </tr>
+     * </table>
+     *
+     * @see android.graphics.ImageFormat
+     */
+    public abstract int getFormat();
+
+    /**
+     * The width of the image in pixels. For formats where some color channels
+     * are subsampled, this is the width of the largest-resolution plane.
+     */
+    public abstract int getWidth();
+
+    /**
+     * The height of the image in pixels. For formats where some color channels
+     * are subsampled, this is the height of the largest-resolution plane.
+     */
+    public abstract int getHeight();
+
+    /**
+     * Get the timestamp associated with this frame.
+     * <p>
+     * The timestamp is measured in nanoseconds, and is monotonically
+     * increasing. However, the zero point and whether the timestamp can be
+     * compared against other sources of time or images depend on the source of
+     * this image.
+     * </p>
+     */
+    public abstract long getTimestamp();
+
+    private Rect mCropRect;
+
+    /**
+     * Get the crop rectangle associated with this frame.
+     * <p>
+     * The crop rectangle specifies the region of valid pixels in the image,
+     * using coordinates in the largest-resolution plane.
+     */
+    public Rect getCropRect() {
+        if (mCropRect == null) {
+            return new Rect(0, 0, getWidth(), getHeight());
+        } else {
+            return new Rect(mCropRect); // return a copy
+        }
+    }
+
+    /**
+     * Set the crop rectangle associated with this frame.
+     * <p>
+     * The crop rectangle specifies the region of valid pixels in the image,
+     * using coordinates in the largest-resolution plane.
+     */
+    public void setCropRect(Rect cropRect) {
+        if (cropRect != null) {
+            cropRect = new Rect(cropRect);  // make a copy
+            cropRect.intersect(0, 0, getWidth(), getHeight());
+        }
+        mCropRect = cropRect;
+    }
+
+    /**
+     * Get the array of pixel planes for this CodecImage. The number of planes is
+     * determined by the format of the CodecImage.
+     */
+    public abstract Plane[] getPlanes();
+
+    /**
+     * Free up this frame for reuse.
+     * <p>
+     * After calling this method, calling any methods on this {@code CodecImage} will
+     * result in an {@link IllegalStateException}, and attempting to read from
+     * {@link ByteBuffer ByteBuffers} returned by an earlier
+     * {@link Plane#getBuffer} call will have undefined behavior.
+     * </p>
+     */
+    @Override
+    public abstract void close();
+
+    /**
+     * <p>A single color plane of image data.</p>
+     *
+     * <p>The number and meaning of the planes in an CodecImage are determined by the
+     * format of the CodecImage.</p>
+     *
+     * <p>Once the CodecImage has been closed, any access to the the plane's
+     * ByteBuffer will fail.</p>
+     *
+     * @see #getFormat
+     */
+    public static abstract class Plane {
+        /**
+         * <p>The row stride for this color plane, in bytes.</p>
+         *
+         * <p>This is the distance between the start of two consecutive rows of
+         * pixels in the image. The row stride is always greater than 0.</p>
+         */
+        public abstract int getRowStride();
+        /**
+         * <p>The distance between adjacent pixel samples, in bytes.</p>
+         *
+         * <p>This is the distance between two consecutive pixel values in a row
+         * of pixels. It may be larger than the size of a single pixel to
+         * account for interleaved image data or padded formats.
+         * The pixel stride is always greater than 0.</p>
+         */
+        public abstract int getPixelStride();
+        /**
+         * <p>Get a direct {@link java.nio.ByteBuffer ByteBuffer}
+         * containing the frame data.</p>
+         *
+         * <p>In particular, the buffer returned will always have
+         * {@link java.nio.ByteBuffer#isDirect isDirect} return {@code true}, so
+         * the underlying data could be mapped as a pointer in JNI without doing
+         * any copies with {@code GetDirectBufferAddress}.</p>
+         *
+         * @return the byte buffer containing the image data for this plane.
+         */
+        public abstract ByteBuffer getBuffer();
+    }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index cd6b68f..db10cd1 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -33,6 +33,9 @@
 
     private boolean mSawInputEOS, mSawOutputEOS;
     private boolean mLimitQueueDepth;
+    private boolean mTunneled;
+    private boolean mIsAudio;
+    private int mAudioSessionId;
     private ByteBuffer[] mCodecInputBuffers;
     private ByteBuffer[] mCodecOutputBuffers;
     private int mTrackIndex;
@@ -40,8 +43,9 @@
     private LinkedList<Integer> mAvailableOutputBufferIndices;
     private LinkedList<MediaCodec.BufferInfo> mAvailableOutputBufferInfos;
     private long mPresentationTimeUs;
+    private long mSampleBaseTimeUs;
     private MediaCodec mCodec;
-    private MediaCodecCencPlayer mMediaCodecPlayer;
+    private MediaTimeProvider mMediaTimeProvider;
     private MediaExtractor mExtractor;
     private MediaFormat mFormat;
     private MediaFormat mOutputFormat;
@@ -51,19 +55,23 @@
      * Manages audio and video playback using MediaCodec and AudioTrack.
      */
     public CodecState(
-            MediaCodecCencPlayer mediaCodecPlayer,
+            MediaTimeProvider mediaTimeProvider,
             MediaExtractor extractor,
             int trackIndex,
             MediaFormat format,
             MediaCodec codec,
-            boolean limitQueueDepth) {
-
-        mMediaCodecPlayer = mediaCodecPlayer;
+            boolean limitQueueDepth,
+            boolean tunneled,
+            int audioSessionId) {
+        mMediaTimeProvider = mediaTimeProvider;
         mExtractor = extractor;
         mTrackIndex = trackIndex;
         mFormat = format;
         mSawInputEOS = mSawOutputEOS = false;
         mLimitQueueDepth = limitQueueDepth;
+        mTunneled = tunneled;
+        mAudioSessionId = audioSessionId;
+        mSampleBaseTimeUs = -1;
 
         mCodec = codec;
 
@@ -72,6 +80,10 @@
         mAvailableOutputBufferInfos = new LinkedList<MediaCodec.BufferInfo>();
 
         mPresentationTimeUs = 0;
+
+        String mime = mFormat.getString(MediaFormat.KEY_MIME);
+        Log.d(TAG, "CodecState::onOutputFormatChanged " + mime);
+        mIsAudio = mime.startsWith("audio/");
     }
 
     public void release() {
@@ -100,7 +112,9 @@
     public void start() {
         mCodec.start();
         mCodecInputBuffers = mCodec.getInputBuffers();
-        mCodecOutputBuffers = mCodec.getOutputBuffers();
+        if (!mTunneled || mIsAudio) {
+            mCodecOutputBuffers = mCodec.getOutputBuffers();
+        }
 
         if (mAudioTrack != null) {
             mAudioTrack.play();
@@ -119,15 +133,17 @@
 
     public void flush() {
         mAvailableInputBufferIndices.clear();
-        mAvailableOutputBufferIndices.clear();
-        mAvailableOutputBufferInfos.clear();
+        if (!mTunneled || mIsAudio) {
+            mAvailableOutputBufferIndices.clear();
+            mAvailableOutputBufferInfos.clear();
+        }
 
         mSawInputEOS = false;
         mSawOutputEOS = false;
 
         if (mAudioTrack != null
-                && mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
-            mAudioTrack.play();
+                && mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
+            mAudioTrack.flush();
         }
 
         mCodec.flush();
@@ -153,20 +169,22 @@
         while (feedInputBuffer()) {
         }
 
-        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
-        int indexOutput = mCodec.dequeueOutputBuffer(info, 0 /* timeoutUs */);
+        if (mIsAudio || !mTunneled) {
+            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+            int indexOutput = mCodec.dequeueOutputBuffer(info, 0 /* timeoutUs */);
 
-        if (indexOutput == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-            mOutputFormat = mCodec.getOutputFormat();
-            onOutputFormatChanged();
-        } else if (indexOutput == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-            mCodecOutputBuffers = mCodec.getOutputBuffers();
-        } else if (indexOutput != MediaCodec.INFO_TRY_AGAIN_LATER) {
-            mAvailableOutputBufferIndices.add(indexOutput);
-            mAvailableOutputBufferInfos.add(info);
-        }
+            if (indexOutput == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                mOutputFormat = mCodec.getOutputFormat();
+                onOutputFormatChanged();
+            } else if (indexOutput == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                mCodecOutputBuffers = mCodec.getOutputBuffers();
+            } else if (indexOutput != MediaCodec.INFO_TRY_AGAIN_LATER) {
+                mAvailableOutputBufferIndices.add(indexOutput);
+                mAvailableOutputBufferInfos.add(info);
+            }
 
-        while (drainOutputBuffer()) {
+            while (drainOutputBuffer()) {
+            }
         }
     }
 
@@ -200,9 +218,24 @@
                 Log.d(TAG, "sampleSize: " + sampleSize + " trackIndex:" + trackIndex +
                         " sampleTime:" + sampleTime + " sampleFlags:" + sampleFlags);
                 mSawInputEOS = true;
+                // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
+                // we should stream duration
+                if (mTunneled && !mIsAudio) {
+                    mSawOutputEOS = true;
+                }
                 return false;
             }
 
+            if (mTunneled && !mIsAudio) {
+                if (mSampleBaseTimeUs == -1) {
+                    mSampleBaseTimeUs = sampleTime;
+                }
+                sampleTime -= mSampleBaseTimeUs;
+                // FIX-ME: in tunneled mode we currently use input buffer time
+                // as video presentation time. This is not accurate and should be fixed
+                mPresentationTimeUs = sampleTime;
+            }
+
             if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
                 MediaCodec.CryptoInfo info = new MediaCodec.CryptoInfo();
                 mExtractor.getSampleCryptoInfo(info);
@@ -222,6 +255,9 @@
             Log.d(TAG, "saw input EOS on track " + mTrackIndex);
 
             mSawInputEOS = true;
+            if (mTunneled && !mIsAudio) {
+                mSawOutputEOS = true;
+            }
 
             mCodec.queueInputBuffer(
                     index, 0 /* offset */, 0 /* sampleSize */,
@@ -255,7 +291,8 @@
                     sampleRate < 8000 || sampleRate > 128000) {
                 return;
             }
-            mAudioTrack = new NonBlockingAudioTrack(sampleRate, channelCount);
+            mAudioTrack = new NonBlockingAudioTrack(sampleRate, channelCount,
+                                    mTunneled, mAudioSessionId);
             mAudioTrack.play();
         }
 
@@ -285,21 +322,19 @@
         }
 
         long realTimeUs =
-            mMediaCodecPlayer.getRealTimeUsForMediaTime(info.presentationTimeUs);
+            mMediaTimeProvider.getRealTimeUsForMediaTime(info.presentationTimeUs);
 
-        long nowUs = mMediaCodecPlayer.getNowUs();
+        long nowUs = mMediaTimeProvider.getNowUs();
 
         long lateUs = nowUs - realTimeUs;
 
         if (mAudioTrack != null) {
             ByteBuffer buffer = mCodecOutputBuffers[index];
             buffer.clear();
-            buffer.position(0 /* offset */);
+            ByteBuffer audioBuffer = ByteBuffer.allocate(buffer.remaining());
+            audioBuffer.put(buffer);
 
-            byte[] audioCopy = new byte[info.size];
-            buffer.get(audioCopy, 0, info.size);
-
-            mAudioTrack.write(audioCopy, info.size);
+            mAudioTrack.write(audioBuffer, info.size, info.presentationTimeUs*1000);
 
             mCodec.releaseOutputBuffer(index, false /* render */);
 
diff --git a/tests/tests/media/src/android/media/cts/CodecUtils.java b/tests/tests/media/src/android/media/cts/CodecUtils.java
new file mode 100644
index 0000000..ef20e67
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/CodecUtils.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.graphics.Rect;
+import android.media.cts.CodecImage;
+import android.media.Image;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+public class CodecUtils  {
+    private static final String TAG = "CodecUtils";
+
+    /** Load jni on initialization */
+    static {
+        Log.i(TAG, "before loadlibrary");
+        System.loadLibrary("ctsmediacodec_jni");
+        Log.i(TAG, "after loadlibrary");
+    }
+
+    private static class ImageWrapper extends CodecImage {
+        private final Image mImage;
+        private final Plane[] mPlanes;
+
+        private ImageWrapper(Image image) {
+            mImage = image;
+            Image.Plane[] planes = mImage.getPlanes();
+
+            mPlanes = new Plane[planes.length];
+            for (int i = 0; i < planes.length; i++) {
+                mPlanes[i] = new PlaneWrapper(planes[i]);
+            }
+        }
+
+        public static ImageWrapper createFromImage(Image image) {
+            return new ImageWrapper(image);
+        }
+
+        @Override
+        public int getFormat() {
+            return mImage.getFormat();
+        }
+
+        @Override
+        public int getWidth() {
+            return mImage.getWidth();
+        }
+
+        @Override
+        public int getHeight() {
+            return mImage.getHeight();
+        }
+
+        @Override
+        public long getTimestamp() {
+            return mImage.getTimestamp();
+        }
+
+        @Override
+        public Plane[] getPlanes() {
+            return mPlanes;
+        }
+
+        @Override
+        public void close() {
+            mImage.close();
+        }
+
+        private static class PlaneWrapper extends CodecImage.Plane {
+            private final Image.Plane mPlane;
+
+            PlaneWrapper(Image.Plane plane) {
+                mPlane = plane;
+            }
+
+            @Override
+            public int getRowStride() {
+                return mPlane.getRowStride();
+            }
+
+           @Override
+            public int getPixelStride() {
+               return mPlane.getPixelStride();
+            }
+
+            @Override
+            public ByteBuffer getBuffer() {
+                return mPlane.getBuffer();
+            }
+        }
+    }
+
+
+    public native static int getImageChecksum(CodecImage image);
+    public native static void copyFlexYUVImage(CodecImage target, CodecImage source);
+
+    public static void copyFlexYUVImage(Image target, CodecImage source) {
+        copyFlexYUVImage(ImageWrapper.createFromImage(target), source);
+    }
+    public static void copyFlexYUVImage(Image target, Image source) {
+        copyFlexYUVImage(
+                ImageWrapper.createFromImage(target),
+                ImageWrapper.createFromImage(source));
+    }
+
+    public native static void fillImageRectWithYUV(
+            CodecImage image, Rect area, int y, int u, int v);
+
+    public static void fillImageRectWithYUV(Image image, Rect area, int y, int u, int v) {
+        fillImageRectWithYUV(ImageWrapper.createFromImage(image), area, y, u, v);
+    }
+
+    public native static long[] getRawStats(CodecImage image, Rect area);
+
+    public static long[] getRawStats(Image image, Rect area) {
+        return getRawStats(ImageWrapper.createFromImage(image), area);
+    }
+
+    public native static float[] getYUVStats(CodecImage image, Rect area);
+
+    public static float[] getYUVStats(Image image, Rect area) {
+        return getYUVStats(ImageWrapper.createFromImage(image), area);
+    }
+
+    public native static float[] Raw2YUVStats(long[] rawStats);
+}
+
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
old mode 100644
new mode 100755
index bf5aa4a..40e619b
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -25,12 +25,16 @@
 import android.cts.util.MediaUtils;
 import android.graphics.ImageFormat;
 import android.media.Image;
+import android.media.AudioManager;
 import android.media.MediaCodec;
+import android.media.MediaCodecList;
 import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.util.Log;
 import android.view.Surface;
+import android.net.Uri;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
@@ -40,6 +44,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.zip.CRC32;
+import java.util.concurrent.TimeUnit;
 
 public class DecoderTest extends MediaPlayerTestBase {
     private static final String TAG = "DecoderTest";
@@ -57,6 +62,24 @@
     private Resources mResources;
     short[] mMasterBuffer;
 
+    private MediaCodecTunneledPlayer mMediaCodecPlayer;
+    private static final int SLEEP_TIME_MS = 1000;
+    private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
+    private static final Uri AUDIO_URL = Uri.parse(
+            "http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
+                + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+                + "&sparams=ip,ipbits,expire,id,itag,source"
+                + "&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26."
+                + "49582D382B4A9AFAA163DED38D2AE531D85603C0"
+                + "&key=ik0&user=android-device-test");  // H.264 Base + AAC
+    private static final Uri VIDEO_URL = Uri.parse(
+            "http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
+                + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+                + "&sparams=ip,ipbits,expire,id,itag,source"
+                + "&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26."
+                + "49582D382B4A9AFAA163DED38D2AE531D85603C0"
+                + "&key=ik0&user=android-device-test");  // H.264 Base + AAC
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -907,6 +930,12 @@
         }
     }
 
+    public void testH264SecureDecode30fps1280x720Tv() throws Exception {
+        if (checkTv()) {
+            verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30);
+        }
+    }
+
     public void testH264Decode30fps1280x720() throws Exception {
         testDecode(R.raw.video_1280x720_mp4_h264_8192kbps_30fps_aac_stereo_128kbps_44100hz, 299);
     }
@@ -917,6 +946,12 @@
         }
     }
 
+    public void testH264SecureDecode60fps1280x720Tv() throws Exception {
+        if (checkTv()) {
+            verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60);
+        }
+    }
+
     public void testH264Decode60fps1280x720() throws Exception {
         testDecode(R.raw.video_1280x720_mp4_h264_8192kbps_60fps_aac_stereo_128kbps_44100hz, 596);
     }
@@ -927,6 +962,12 @@
         }
     }
 
+    public void testH264SecureDecode30fps1920x1080Tv() throws Exception {
+        if (checkTv()) {
+            verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30);
+        }
+    }
+
     public void testH264Decode30fps1920x1080() throws Exception {
         testDecode(R.raw.video_1920x1080_mp4_h264_20480kbps_30fps_aac_stereo_128kbps_44100hz, 299);
     }
@@ -937,12 +978,18 @@
         }
     }
 
+    public void testH264SecureDecode60fps1920x1080Tv() throws Exception {
+        if (checkTv()) {
+            verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60);
+        }
+    }
+
     public void testH264Decode60fps1920x1080() throws Exception {
         testDecode(R.raw.video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz, 596);
     }
 
     public void testVP8Decode320x240() throws Exception {
-        testDecode(R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
+        testDecode(R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
     }
 
     public void testVP8Decode640x360() throws Exception {
@@ -987,7 +1034,7 @@
     }
 
     public void testVP8Decode60fps1920x1080() throws Exception {
-        testDecode(R.raw.video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz,
+        testDecode(R.raw.video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz,
                 249);
     }
 
@@ -1006,7 +1053,7 @@
     }
 
     public void testVP9Decode30fps1280x720() throws Exception {
-        testDecode(R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
+        testDecode(R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
     }
 
     public void testVP9Decode30fps1920x1080() throws Exception {
@@ -1074,7 +1121,7 @@
 
     public void testCodecEarlyEOSHEVC() throws Exception {
         testCodecEarlyEOS(
-                R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
+                R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz,
                 120 /* eosframe */);
     }
 
@@ -1196,6 +1243,23 @@
         assertEquals("different number of frames when using flushed codec", frames1, frames3);
     }
 
+    private static void verifySecureVideoDecodeSupport(String mime, int width, int height, float rate) {
+        MediaFormat baseFormat = new MediaFormat();
+        baseFormat.setString(MediaFormat.KEY_MIME, mime);
+        baseFormat.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true);
+
+        MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
+        format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true);
+        format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
+
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+        if (mcl.findDecoderForFormat(baseFormat) == null) {
+            MediaUtils.skipTest("no secure decoder for " + mime);
+            return;
+        }
+        assertNotNull("no decoder for " + format, mcl.findDecoderForFormat(format));
+    }
+
     private static MediaCodec createDecoder(String mime) {
         try {
             if (false) {
@@ -1220,6 +1284,10 @@
         }
     }
 
+    private static MediaCodec createDecoder(MediaFormat format) {
+        return MediaUtils.getDecoder(format);
+    }
+
     // for video
     private int countFrames(int video, int resetMode, int eosframe, Surface s)
             throws Exception {
@@ -1332,7 +1400,7 @@
         ByteBuffer[] codecInputBuffers;
         ByteBuffer[] codecOutputBuffers;
 
-        MediaCodec codec = createDecoder(mime);
+        MediaCodec codec = createDecoder(format);
         Log.i("@@@@", "using codec: " + codec.getName());
         codec.configure(format, surface, null /* crypto */, 0 /* flags */);
         codec.start();
@@ -1590,12 +1658,12 @@
         // Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat));
         switch (colorFormat) {
         // these are the formats we know how to handle for this test
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
+            case CodecCapabilities.COLOR_FormatYUV420Planar:
+            case CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+            case CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+            case CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+            case CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+            case CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
                 /*
                  * TODO: Check newer formats or ignore.
                  * OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002
@@ -1850,5 +1918,81 @@
         return maxvalue;
     }
 
+    /* return true if a particular video feature is supported for the given mimetype */
+    private boolean isVideoFeatureSupported(String mimeType, String feature) {
+        MediaFormat format = MediaFormat.createVideoFormat( mimeType, 1920, 1080);
+        format.setFeatureEnabled(feature, true);
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+        String codecName = mcl.findDecoderForFormat(format);
+        return (codecName == null) ? false : true;
+    }
+
+
+    /**
+     * Test tunneled video playback mode if supported
+     */
+    public void testTunneledVideoPlayback() throws Exception {
+        if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC,
+                CodecCapabilities.FEATURE_TunneledPlayback)) {
+            MediaUtils.skipTest(TAG, "No tunneled video playback codec found!");
+            return;
+        }
+
+        AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+        mMediaCodecPlayer = new MediaCodecTunneledPlayer(
+                getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
+
+        mMediaCodecPlayer.setAudioDataSource(AUDIO_URL, null);
+        mMediaCodecPlayer.setVideoDataSource(VIDEO_URL, null);
+        assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
+        assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+
+        // starts video playback
+        mMediaCodecPlayer.startThread();
+
+        long timeOut = System.currentTimeMillis() + 4*PLAY_TIME_MS;
+        while (timeOut > System.currentTimeMillis() && !mMediaCodecPlayer.isEnded()) {
+            Thread.sleep(SLEEP_TIME_MS);
+            if (mMediaCodecPlayer.getCurrentPosition() >= mMediaCodecPlayer.getDuration() ) {
+                Log.d(TAG, "testTunneledVideoPlayback -- current pos = " +
+                        mMediaCodecPlayer.getCurrentPosition() +
+                        ">= duration = " + mMediaCodecPlayer.getDuration());
+                break;
+            }
+        }
+        assertTrue("Tunneled video playback timeout exceeded!",
+                timeOut > System.currentTimeMillis());
+
+        Log.d(TAG, "playVideo player.reset()");
+        mMediaCodecPlayer.reset();
+    }
+
+    /**
+     * Test tunneled video playback flush if supported
+     */
+    public void testTunneledVideoFlush() throws Exception {
+        if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC,
+                CodecCapabilities.FEATURE_TunneledPlayback)) {
+            MediaUtils.skipTest(TAG, "No tunneled video playback codec found!");
+            return;
+        }
+
+        AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+        mMediaCodecPlayer = new MediaCodecTunneledPlayer(
+                getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
+
+        mMediaCodecPlayer.setAudioDataSource(AUDIO_URL, null);
+        mMediaCodecPlayer.setVideoDataSource(VIDEO_URL, null);
+        assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
+        assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+
+        // starts video playback
+        mMediaCodecPlayer.startThread();
+        Thread.sleep(SLEEP_TIME_MS);
+        mMediaCodecPlayer.pause();
+        mMediaCodecPlayer.flush();
+        Thread.sleep(SLEEP_TIME_MS);
+        mMediaCodecPlayer.reset();
+    }
 }
 
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
index d65bbf6..48fcb65 100755
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
@@ -509,7 +509,7 @@
      * Determines if two color values are approximately equal.
      */
     private static boolean approxEquals(int expected, int actual) {
-        final int MAX_DELTA = 4;
+        final int MAX_DELTA = 7;
         return Math.abs(expected - actual) <= MAX_DELTA;
     }
 
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index 5f326ee..cc28b86 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -21,14 +21,19 @@
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.cts.util.MediaUtils;
+import android.graphics.Rect;
 import android.graphics.ImageFormat;
+import android.media.cts.CodecUtils;
 import android.media.Image;
 import android.media.Image.Plane;
 import android.media.ImageReader;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.os.Handler;
@@ -38,9 +43,15 @@
 import android.util.Log;
 import android.view.Surface;
 
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
+
+import java.io.File;
 import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -62,12 +73,16 @@
     private static final long WAIT_FOR_IMAGE_TIMEOUT_MS = 1000;
     private static final String DEBUG_FILE_NAME_BASE = "/sdcard/";
     private static final int NUM_FRAME_DECODED = 100;
-    private static final int MAX_NUM_IMAGES = 3;
+    // video decoders only support a single outstanding image with the consumer
+    private static final int MAX_NUM_IMAGES = 1;
+    private static final float COLOR_STDEV_ALLOWANCE = 5f;
+    private static final float COLOR_DELTA_ALLOWANCE = 5f;
+
+    private final static int MODE_IMAGEREADER = 0;
+    private final static int MODE_IMAGE       = 1;
 
     private Resources mResources;
     private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
-    private ByteBuffer[] mInputBuffers;
-    private ByteBuffer[] mOutputBuffers;
     private ImageReader mReader;
     private Surface mReaderSurface;
     private HandlerThread mHandlerThread;
@@ -95,31 +110,322 @@
         mHandler = null;
     }
 
-    /**
-     * Test ImageReader with 480x360 hw AVC decoding for flexible yuv format, which is mandatory
-     * to be supported by hw decoder.
-     */
-    public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
-        try {
-            int format = ImageFormat.YUV_420_888;
-            videoDecodeToSurface(
-                    R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
-                    /* width */480, /* height */ 360, format, /* useHw */ true);
-        } finally {
-            closeImageReader();
+    static class MediaAsset {
+        public MediaAsset(int resource, int width, int height) {
+            mResource = resource;
+            mWidth = width;
+            mHeight = height;
+        }
+
+        public int getWidth() {
+            return mWidth;
+        }
+
+        public int getHeight() {
+            return mHeight;
+        }
+
+        public int getResource() {
+            return mResource;
+        }
+
+        private final int mResource;
+        private final int mWidth;
+        private final int mHeight;
+    }
+
+    static class MediaAssets {
+        public MediaAssets(String mime, MediaAsset... assets) {
+            mMime = mime;
+            mAssets = assets;
+        }
+
+        public String getMime() {
+            return mMime;
+        }
+
+        public MediaAsset[] getAssets() {
+            return mAssets;
+        }
+
+        private final String mMime;
+        private final MediaAsset[] mAssets;
+    }
+
+    private static MediaAssets H263_ASSETS = new MediaAssets(
+            MediaFormat.MIMETYPE_VIDEO_H263,
+            new MediaAsset(R.raw.swirl_176x144_h263, 176, 144),
+            new MediaAsset(R.raw.swirl_352x288_h263, 352, 288),
+            new MediaAsset(R.raw.swirl_128x96_h263, 128, 96));
+
+    private static MediaAssets MPEG4_ASSETS = new MediaAssets(
+            MediaFormat.MIMETYPE_VIDEO_MPEG4,
+            new MediaAsset(R.raw.swirl_128x128_mpeg4, 128, 128),
+            new MediaAsset(R.raw.swirl_144x136_mpeg4, 144, 136),
+            new MediaAsset(R.raw.swirl_136x144_mpeg4, 136, 144),
+            new MediaAsset(R.raw.swirl_132x130_mpeg4, 132, 130),
+            new MediaAsset(R.raw.swirl_130x132_mpeg4, 130, 132));
+
+    private static MediaAssets H264_ASSETS = new MediaAssets(
+            MediaFormat.MIMETYPE_VIDEO_AVC,
+            new MediaAsset(R.raw.swirl_128x128_h264, 128, 128),
+            new MediaAsset(R.raw.swirl_144x136_h264, 144, 136),
+            new MediaAsset(R.raw.swirl_136x144_h264, 136, 144),
+            new MediaAsset(R.raw.swirl_132x130_h264, 132, 130),
+            new MediaAsset(R.raw.swirl_130x132_h264, 130, 132));
+
+    private static MediaAssets H265_ASSETS = new MediaAssets(
+            MediaFormat.MIMETYPE_VIDEO_HEVC,
+            new MediaAsset(R.raw.swirl_128x128_h265, 128, 128),
+            new MediaAsset(R.raw.swirl_144x136_h265, 144, 136),
+            new MediaAsset(R.raw.swirl_136x144_h265, 136, 144),
+            new MediaAsset(R.raw.swirl_132x130_h265, 132, 130),
+            new MediaAsset(R.raw.swirl_130x132_h265, 130, 132));
+
+    private static MediaAssets VP8_ASSETS = new MediaAssets(
+            MediaFormat.MIMETYPE_VIDEO_VP8,
+            new MediaAsset(R.raw.swirl_128x128_vp8, 128, 128),
+            new MediaAsset(R.raw.swirl_144x136_vp8, 144, 136),
+            new MediaAsset(R.raw.swirl_136x144_vp8, 136, 144),
+            new MediaAsset(R.raw.swirl_132x130_vp8, 132, 130),
+            new MediaAsset(R.raw.swirl_130x132_vp8, 130, 132));
+
+    private static MediaAssets VP9_ASSETS = new MediaAssets(
+            MediaFormat.MIMETYPE_VIDEO_VP9,
+            new MediaAsset(R.raw.swirl_128x128_vp9, 128, 128),
+            new MediaAsset(R.raw.swirl_144x136_vp9, 144, 136),
+            new MediaAsset(R.raw.swirl_136x144_vp9, 136, 144),
+            new MediaAsset(R.raw.swirl_132x130_vp9, 132, 130),
+            new MediaAsset(R.raw.swirl_130x132_vp9, 130, 132));
+
+    static final float SWIRL_FPS = 12.f;
+
+    class Decoder {
+        final private String mName;
+        final private String mMime;
+        final private VideoCapabilities mCaps;
+        final private ArrayList<MediaAsset> mAssets;
+
+        boolean isFlexibleFormatSupported(CodecCapabilities caps) {
+            for (int c : caps.colorFormats) {
+                if (c == COLOR_FormatYUV420Flexible) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        Decoder(String name, MediaAssets assets, CodecCapabilities caps) {
+            mName = name;
+            mMime = assets.getMime();
+            mCaps = caps.getVideoCapabilities();
+            mAssets = new ArrayList<MediaAsset>();
+
+            for (MediaAsset asset : assets.getAssets()) {
+                if (mCaps.areSizeAndRateSupported(asset.getWidth(), asset.getHeight(), SWIRL_FPS)
+                        && isFlexibleFormatSupported(caps)) {
+                    mAssets.add(asset);
+                }
+            }
+        }
+
+        public boolean videoDecode(int mode, boolean checkSwirl) {
+            boolean skipped = true;
+            for (MediaAsset asset: mAssets) {
+                // TODO: loop over all supported image formats
+                int imageFormat = ImageFormat.YUV_420_888;
+                int colorFormat = COLOR_FormatYUV420Flexible;
+                videoDecode(asset, imageFormat, colorFormat, mode, checkSwirl);
+                skipped = false;
+            }
+            return skipped;
+        }
+
+        private void videoDecode(
+                MediaAsset asset, int imageFormat, int colorFormat, int mode, boolean checkSwirl) {
+            int video = asset.getResource();
+            int width = asset.getWidth();
+            int height = asset.getHeight();
+
+            if (DEBUG) Log.d(TAG, "videoDecode " + mName + " " + width + "x" + height);
+
+            MediaCodec decoder = null;
+            AssetFileDescriptor vidFD = null;
+
+            MediaExtractor extractor = null;
+            File tmpFile = null;
+            InputStream is = null;
+            FileOutputStream os = null;
+            MediaFormat mediaFormat = null;
+            try {
+                extractor = new MediaExtractor();
+
+                try {
+                    vidFD = mResources.openRawResourceFd(video);
+                    extractor.setDataSource(
+                            vidFD.getFileDescriptor(), vidFD.getStartOffset(), vidFD.getLength());
+                } catch (NotFoundException e) {
+                    // resource is compressed, uncompress locally
+                    String tmpName = "tempStream";
+                    tmpFile = File.createTempFile(tmpName, null, mContext.getCacheDir());
+                    is = mResources.openRawResource(video);
+                    os = new FileOutputStream(tmpFile);
+                    byte[] buf = new byte[1024];
+                    int len;
+                    while ((len = is.read(buf, 0, buf.length)) > 0) {
+                        os.write(buf, 0, len);
+                    }
+                    os.close();
+                    is.close();
+
+                    extractor.setDataSource(tmpFile.getAbsolutePath());
+                }
+
+                mediaFormat = extractor.getTrackFormat(0);
+                mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+
+                // Create decoder
+                decoder = MediaCodec.createByCodecName(mName);
+                assertNotNull("couldn't create decoder" + mName, decoder);
+
+                decodeFramesToImage(
+                        decoder, extractor, mediaFormat,
+                        width, height, imageFormat, mode, checkSwirl);
+
+                decoder.stop();
+                if (vidFD != null) {
+                    vidFD.close();
+                }
+            } catch (Throwable e) {
+                throw new RuntimeException("while " + mName + " decoding "
+                        + mResources.getResourceEntryName(video) + ": " + mediaFormat, e);
+            } finally {
+                if (decoder != null) {
+                    decoder.release();
+                }
+                if (extractor != null) {
+                    extractor.release();
+                }
+                if (tmpFile != null) {
+                    tmpFile.delete();
+                }
+            }
         }
     }
 
+    private Decoder[] decoders(MediaAssets assets, boolean goog) {
+        String mime = assets.getMime();
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        ArrayList<Decoder> result = new ArrayList<Decoder>();
+
+        for (MediaCodecInfo info : mcl.getCodecInfos()) {
+            if (info.isEncoder()
+                    || info.getName().toLowerCase().startsWith("omx.google.") != goog) {
+                continue;
+            }
+            CodecCapabilities caps = null;
+            try {
+                caps = info.getCapabilitiesForType(mime);
+            } catch (IllegalArgumentException e) { // mime is not supported
+                continue;
+            }
+            assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
+            result.add(new Decoder(info.getName(), assets, caps));
+        }
+        return result.toArray(new Decoder[result.size()]);
+    }
+
+    private Decoder[] goog(MediaAssets assets) {
+        return decoders(assets, true /* goog */);
+    }
+
+    private Decoder[] other(MediaAssets assets) {
+        return decoders(assets, false /* goog */);
+    }
+
+    private Decoder[] googH265()  { return goog(H265_ASSETS); }
+    private Decoder[] googH264()  { return goog(H264_ASSETS); }
+    private Decoder[] googH263()  { return goog(H263_ASSETS); }
+    private Decoder[] googMpeg4() { return goog(MPEG4_ASSETS); }
+    private Decoder[] googVP8()   { return goog(VP8_ASSETS); }
+    private Decoder[] googVP9()   { return goog(VP9_ASSETS); }
+
+    private Decoder[] otherH265()  { return other(H265_ASSETS); }
+    private Decoder[] otherH264()  { return other(H264_ASSETS); }
+    private Decoder[] otherH263()  { return other(H263_ASSETS); }
+    private Decoder[] otherMpeg4() { return other(MPEG4_ASSETS); }
+    private Decoder[] otherVP8()   { return other(VP8_ASSETS); }
+    private Decoder[] otherVP9()   { return other(VP9_ASSETS); }
+
+    public void testGoogH265Image()   { swirlTest(googH265(),   MODE_IMAGE); }
+    public void testGoogH264Image()   { swirlTest(googH264(),   MODE_IMAGE); }
+    public void testGoogH263Image()   { swirlTest(googH263(),   MODE_IMAGE); }
+    public void testGoogMpeg4Image()  { swirlTest(googMpeg4(),  MODE_IMAGE); }
+    public void testGoogVP8Image()    { swirlTest(googVP8(),    MODE_IMAGE); }
+    public void testGoogVP9Image()    { swirlTest(googVP9(),    MODE_IMAGE); }
+
+    public void testOtherH265Image()  { swirlTest(otherH265(),  MODE_IMAGE); }
+    public void testOtherH264Image()  { swirlTest(otherH264(),  MODE_IMAGE); }
+    public void testOtherH263Image()  { swirlTest(otherH263(),  MODE_IMAGE); }
+    public void testOtherMpeg4Image() { swirlTest(otherMpeg4(), MODE_IMAGE); }
+    public void testOtherVP8Image()   { swirlTest(otherVP8(),   MODE_IMAGE); }
+    public void testOtherVP9Image()   { swirlTest(otherVP9(),   MODE_IMAGE); }
+
+    public void testGoogH265ImageReader()   { swirlTest(googH265(),   MODE_IMAGEREADER); }
+    public void testGoogH264ImageReader()   { swirlTest(googH264(),   MODE_IMAGEREADER); }
+    public void testGoogH263ImageReader()   { swirlTest(googH263(),   MODE_IMAGEREADER); }
+    public void testGoogMpeg4ImageReader()  { swirlTest(googMpeg4(),  MODE_IMAGEREADER); }
+    public void testGoogVP8ImageReader()    { swirlTest(googVP8(),    MODE_IMAGEREADER); }
+    public void testGoogVP9ImageReader()    { swirlTest(googVP9(),    MODE_IMAGEREADER); }
+
+    public void testOtherH265ImageReader()  { swirlTest(otherH265(),  MODE_IMAGEREADER); }
+    public void testOtherH264ImageReader()  { swirlTest(otherH264(),  MODE_IMAGEREADER); }
+    public void testOtherH263ImageReader()  { swirlTest(otherH263(),  MODE_IMAGEREADER); }
+    public void testOtherMpeg4ImageReader() { swirlTest(otherMpeg4(), MODE_IMAGEREADER); }
+    public void testOtherVP8ImageReader()   { swirlTest(otherVP8(),   MODE_IMAGEREADER); }
+    public void testOtherVP9ImageReader()   { swirlTest(otherVP9(),   MODE_IMAGEREADER); }
+
     /**
-     * Test ImageReader with 480x360 sw AVC decoding for flexible yuv format, which is mandatory
-     * to be supported by sw decoder.
+     * Test ImageReader with 480x360 non-google AVC decoding for flexible yuv format
+     */
+    public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
+        Decoder[] decoders = other(new MediaAssets(
+                MediaFormat.MIMETYPE_VIDEO_AVC,
+                new MediaAsset(
+                        R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                        480 /* width */, 360 /* height */)));
+
+        decodeTest(decoders, MODE_IMAGEREADER, false /* checkSwirl */);
+    }
+
+    /**
+     * Test ImageReader with 480x360 google (SW) AVC decoding for flexible yuv format
      */
     public void testSwAVCDecode360pForFlexibleYuv() throws Exception {
+        Decoder[] decoders = goog(new MediaAssets(
+                MediaFormat.MIMETYPE_VIDEO_AVC,
+                new MediaAsset(
+                        R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                        480 /* width */, 360 /* height */)));
+
+        decodeTest(decoders, MODE_IMAGEREADER, false /* checkSwirl */);
+    }
+
+    private void swirlTest(Decoder[] decoders, int mode) {
+        decodeTest(decoders, mode, true /* checkSwirl */);
+    }
+
+    private void decodeTest(Decoder[] decoders, int mode, boolean checkSwirl) {
         try {
-            int format = ImageFormat.YUV_420_888;
-            videoDecodeToSurface(
-                    R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
-                    /* width */ 480, /* height */ 360, format, /* useHw */ false);
+            boolean skipped = true;
+            for (Decoder codec : decoders) {
+                if (codec.videoDecode(mode, checkSwirl)) {
+                    skipped = false;
+                }
+            }
+            if (skipped) {
+                MediaUtils.skipTest("decoder does not any of the input files");
+            }
         } finally {
             closeImageReader();
         }
@@ -152,61 +458,26 @@
         }
     }
 
-    private void videoDecodeToSurface(int video, int width,
-            int height, int imageFormat, boolean useHw) throws Exception {
-        MediaCodec decoder = null;
-        MediaExtractor extractor;
-        int outputBufferIndex;
-        ByteBuffer[] decoderInputBuffers;
-        ByteBuffer[] decoderOutputBuffers;
-
-        if (!MediaUtils.checkCodecForResource(mContext, video, 0 /* track */)) {
-            return; // skip
-        }
-
-        AssetFileDescriptor vidFD = mResources.openRawResourceFd(video);
-
-        extractor = new MediaExtractor();
-        extractor.setDataSource(vidFD.getFileDescriptor(), vidFD.getStartOffset(),
-                vidFD.getLength());
-
-        MediaFormat mediaFmt = extractor.getTrackFormat(0);
-        mediaFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                            CodecCapabilities.COLOR_FormatYUV420Flexible);
-        String mime = mediaFmt.getString(MediaFormat.KEY_MIME);
-        try {
-            // Create decoder
-            decoder = createDecoder(mime, useHw);
-            assertNotNull("couldn't find decoder", decoder);
-            if (VERBOSE) Log.v(TAG, "using decoder: " + decoder.getName());
-
-            decodeFramesToImageReader(width, height, imageFormat, decoder,
-                    extractor, mediaFmt, mime);
-
-            decoder.stop();
-        } finally {
-            decoder.release();
-        }
-
-    }
-
     /**
      * Decode video frames to image reader.
      */
-    private void decodeFramesToImageReader(int width, int height, int imageFormat,
-            MediaCodec decoder, MediaExtractor extractor, MediaFormat mediaFmt, String mime)
-            throws Exception, InterruptedException {
+    private void decodeFramesToImage(
+            MediaCodec decoder, MediaExtractor extractor, MediaFormat mediaFormat,
+            int width, int height, int imageFormat, int mode, boolean checkSwirl)
+            throws InterruptedException {
         ByteBuffer[] decoderInputBuffers;
         ByteBuffer[] decoderOutputBuffers;
-        if (!imageFormatSupported(decoder, imageFormat, mime)) {
-            // TODO: SKIPPING TEST
-            return;
-        }
-        createImageReader(width, height, imageFormat, MAX_NUM_IMAGES, mImageListener);
 
         // Configure decoder.
-        if (VERBOSE) Log.v(TAG, "stream format: " + mediaFmt);
-        decoder.configure(mediaFmt, mReaderSurface, /*crypto*/null, /*flags*/0);
+        if (VERBOSE) Log.v(TAG, "stream format: " + mediaFormat);
+        if (mode == MODE_IMAGEREADER) {
+            createImageReader(width, height, imageFormat, MAX_NUM_IMAGES, mImageListener);
+            decoder.configure(mediaFormat, mReaderSurface, null /* crypto */, 0 /* flags */);
+        } else {
+            assertEquals(mode, MODE_IMAGE);
+            decoder.configure(mediaFormat, null /* surface */, null /* crypto */, 0 /* flags */);
+        }
+
         decoder.start();
         decoderInputBuffers = decoder.getInputBuffers();
         decoderOutputBuffers = decoder.getOutputBuffers();
@@ -271,65 +542,55 @@
                 // Should be decoding error.
                 fail("unexpected result from deocder.dequeueOutputBuffer: " + res);
             } else {
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    sawOutputEOS = true;
+                }
+
                 // res >= 0: normal decoding case, copy the output buffer.
                 // Will use it as reference to valid the ImageReader output
                 // Some decoders output a 0-sized buffer at the end. Ignore those.
-                outputFrameCount++;
                 boolean doRender = (info.size != 0);
 
-                decoder.releaseOutputBuffer(res, doRender);
                 if (doRender) {
-                    // Read image and verify
-                    Image image = mImageListener.getImage(WAIT_FOR_IMAGE_TIMEOUT_MS);
-                    Plane[] imagePlanes = image.getPlanes();
+                    outputFrameCount++;
+                    String fileName = DEBUG_FILE_NAME_BASE + MediaUtils.getTestName()
+                            + (mode == MODE_IMAGE ? "_image_" : "_reader_")
+                            + width + "x" + height + "_" + outputFrameCount + ".yuv";
 
-                    //Verify
-                    String fileName = DEBUG_FILE_NAME_BASE + width + "x" + height + "_"
-                            + outputFrameCount + ".yuv";
-                    validateImage(image, width, height, imageFormat, fileName);
+                    Image image = null;
+                    try {
+                        if (mode == MODE_IMAGE) {
+                            image = decoder.getOutputImage(res);
+                        } else {
+                            decoder.releaseOutputBuffer(res, doRender);
+                            res = -1;
+                            // Read image and verify
+                            image = mImageListener.getImage(WAIT_FOR_IMAGE_TIMEOUT_MS);
+                        }
+                        validateImage(image, width, height, imageFormat, fileName);
 
-                    if (VERBOSE) {
-                        Log.v(TAG, "Image " + outputFrameCount + " Info:");
-                        Log.v(TAG, "first plane pixelstride " + imagePlanes[0].getPixelStride());
-                        Log.v(TAG, "first plane rowstride " + imagePlanes[0].getRowStride());
-                        Log.v(TAG, "Image timestamp:" + image.getTimestamp());
+                        if (checkSwirl) {
+                            try {
+                                validateSwirl(image);
+                            } catch (Throwable e) {
+                                dumpFile(fileName, getDataFromImage(image));
+                                throw e;
+                            }
+                        }
+                    } finally {
+                        if (image != null) {
+                            image.close();
+                        }
                     }
-                    image.close();
+                }
+
+                if (res >= 0) {
+                    decoder.releaseOutputBuffer(res, false /* render */);
                 }
             }
         }
     }
 
-    private boolean imageFormatSupported(MediaCodec decoder, int imageFormat, String mime) {
-        MediaCodecInfo codecInfo = decoder.getCodecInfo();
-        if (codecInfo == null) {
-            return false;
-        }
-        MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mime);
-        for (int colorFormat : capabilities.colorFormats) {
-            if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible
-                    && imageFormat == ImageFormat.YUV_420_888) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private MediaCodec createDecoder(String mime, boolean useHw) throws Exception {
-        if (!useHw) {
-            if (mime.contains("avc")) {
-                return MediaCodec.createByCodecName("OMX.google.h264.decoder");
-            } else if (mime.contains("3gpp")) {
-                return MediaCodec.createByCodecName("OMX.google.h263.decoder");
-            } else if (mime.contains("mp4v")) {
-                return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
-            } else if (mime.contains("vp8")) {
-                return MediaCodec.createByCodecName("OMX.google.vpx.decoder");
-            }
-        }
-        return MediaCodec.createDecoderByType(mime);
-    }
-
     /**
      * Validate image based on format and size.
      *
@@ -339,22 +600,103 @@
      * @param format The image format.
      * @param filePath The debug dump file path, null if don't want to dump to file.
      */
-    public static void validateImage(Image image, int width, int height, int format,
-            String filePath) {
+    public static void validateImage(
+            Image image, int width, int height, int format, String filePath) {
+        if (VERBOSE) {
+            Plane[] imagePlanes = image.getPlanes();
+            Log.v(TAG, "Image " + filePath + " Info:");
+            Log.v(TAG, "first plane pixelstride " + imagePlanes[0].getPixelStride());
+            Log.v(TAG, "first plane rowstride " + imagePlanes[0].getRowStride());
+            Log.v(TAG, "Image timestamp:" + image.getTimestamp());
+        }
+
         assertNotNull("Input image is invalid", image);
         assertEquals("Format doesn't match", format, image.getFormat());
-        assertEquals("Width doesn't match", width, image.getWidth());
-        assertEquals("Height doesn't match", height, image.getHeight());
+        assertEquals("Width doesn't match", width, image.getCropRect().width());
+        assertEquals("Height doesn't match", height, image.getCropRect().height());
 
         if(VERBOSE) Log.v(TAG, "validating Image");
         byte[] data = getDataFromImage(image);
         assertTrue("Invalid image data", data != null && data.length > 0);
 
-        validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
+        validateYuvData(data, width, height, format, image.getTimestamp());
+
+        if (VERBOSE && filePath != null) {
+            dumpFile(filePath, data);
+        }
+    }
+
+    private static void validateSwirl(Image image) {
+        Rect crop = image.getCropRect();
+        final int NUM_SIDES = 4;
+        final int step = 8;      // the width of the layers
+        long[][] rawStats = new long[NUM_SIDES][10];
+        int[][] colors = new int[][] {
+            { 111, 96, 204 }, { 178, 27, 174 }, { 100, 192, 92 }, { 106, 117, 62 }
+        };
+
+        // successively accumulate statistics for each layer of the swirl
+        // by using overlapping rectangles, and the observation that
+        // layer_i = rectangle_i - rectangle_(i+1)
+        int lastLayer = 0;
+        int layer = 0;
+        boolean lastLayerValid = false;
+        for (int pos = 0; ; pos += step) {
+            Rect area = new Rect(pos - step, pos, crop.width() / 2, crop.height() + 2 * step - pos);
+            if (area.isEmpty()) {
+                break;
+            }
+            area.offset(crop.left, crop.top);
+            area.intersect(crop);
+            for (int lr = 0; lr < 2; ++lr) {
+                long[] oneStat = CodecUtils.getRawStats(image, area);
+                if (VERBOSE) Log.v(TAG, "area=" + area + ", layer=" + layer + ", last="
+                                    + lastLayer + ": " + Arrays.toString(oneStat));
+                for (int i = 0; i < oneStat.length; i++) {
+                    rawStats[layer][i] += oneStat[i];
+                    if (lastLayerValid) {
+                        rawStats[lastLayer][i] -= oneStat[i];
+                    }
+                }
+                if (VERBOSE && lastLayerValid) {
+                    Log.v(TAG, "layer-" + lastLayer + ": " + Arrays.toString(rawStats[lastLayer]));
+                    Log.v(TAG, Arrays.toString(CodecUtils.Raw2YUVStats(rawStats[lastLayer])));
+                }
+                // switch to the opposite side
+                layer ^= 2;      // NUM_SIDES / 2
+                lastLayer ^= 2;  // NUM_SIDES / 2
+                area.offset(crop.centerX() - area.left, 2 * (crop.centerY() - area.centerY()));
+            }
+
+            lastLayer = layer;
+            lastLayerValid = true;
+            layer = (layer + 1) % NUM_SIDES;
+        }
+
+        for (layer = 0; layer < NUM_SIDES; ++layer) {
+            float[] stats = CodecUtils.Raw2YUVStats(rawStats[layer]);
+            if (DEBUG) Log.d(TAG, "layer-" + layer + ": " + Arrays.toString(stats));
+            if (VERBOSE) Log.v(TAG, Arrays.toString(rawStats[layer]));
+
+            // check layer uniformity
+            for (int i = 0; i < 3; i++) {
+                assertTrue("color of layer-" + layer + " is not uniform: "
+                        + Arrays.toString(stats),
+                        stats[3 + i] < COLOR_STDEV_ALLOWANCE);
+            }
+
+            // check layer color
+            for (int i = 0; i < 3; i++) {
+                assertTrue("color of layer-" + layer + " mismatches target "
+                        + Arrays.toString(colors[layer]) + " vs "
+                        + Arrays.toString(Arrays.copyOf(stats, 3)),
+                        Math.abs(stats[i] - colors[layer][i]) < COLOR_DELTA_ALLOWANCE);
+            }
+        }
     }
 
     private static void validateYuvData(byte[] yuvData, int width, int height, int format,
-            long ts, String fileName) {
+            long ts) {
 
         assertTrue("YUV format must be one of the YUV_420_888, NV21, or YV12",
                 format == ImageFormat.YUV_420_888 ||
@@ -364,10 +706,6 @@
         if (VERBOSE) Log.v(TAG, "Validating YUV data");
         int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
         assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
-
-        if (DEBUG && fileName != null) {
-            dumpFile(fileName, yuvData);
-        }
     }
 
     private static void checkYuvFormat(int format) {
@@ -412,9 +750,10 @@
      */
     private static byte[] getDataFromImage(Image image) {
         assertNotNull("Invalid image:", image);
+        Rect crop = image.getCropRect();
         int format = image.getFormat();
-        int width = image.getWidth();
-        int height = image.getHeight();
+        int width = crop.width();
+        int height = crop.height();
         int rowStride, pixelStride;
         byte[] data = null;
 
@@ -432,6 +771,7 @@
         byte[] rowData = new byte[planes[0].getRowStride()];
         if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes");
         for (int i = 0; i < planes.length; i++) {
+            int shift = (i == 0) ? 0 : 1;
             buffer = planes[i].getBuffer();
             assertNotNull("Fail to get bytebuffer from plane", buffer);
             rowStride = planes[i].getRowStride();
@@ -444,27 +784,32 @@
                 Log.v(TAG, "height " + height);
             }
             // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
-            int w = (i == 0) ? width : width / 2;
-            int h = (i == 0) ? height : height / 2;
+            int w = crop.width() >> shift;
+            int h = crop.height() >> shift;
+            buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));
             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);
         }
@@ -491,8 +836,9 @@
         }
     }
 
-    private void createImageReader(int width, int height, int format, int maxNumImages,
-            ImageReader.OnImageAvailableListener listener) throws Exception {
+    private void createImageReader(
+            int width, int height, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener)  {
         closeImageReader();
 
         mReader = ImageReader.newInstance(width, height, format, maxNumImages);
diff --git a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
new file mode 100644
index 0000000..be5099e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.Context;
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.audiofx.LoudnessEnhancer;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class LoudnessEnhancerTest extends PostProcTestBase {
+
+    private String TAG = "LoudnessEnhancerTest";
+    private LoudnessEnhancer mLE;
+
+    //-----------------------------------------------------------------
+    // LOUDNESS ENHANCER TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    public void test0_0ConstructorAndRelease() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+        assertNotNull("null AudioManager", am);
+        getLoudnessEnhancer(0);
+        releaseLoudnessEnhancer();
+
+        int session = am.generateAudioSessionId();
+        assertTrue("cannot generate new session", session != AudioManager.ERROR);
+        getLoudnessEnhancer(session);
+        releaseLoudnessEnhancer();
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test set/get target gain
+    public void test1_0TargetGain() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        getLoudnessEnhancer(0);
+        try {
+            mLE.setTargetGain(0);
+            assertEquals("target gain differs from value set", 0.0f, mLE.getTargetGain());
+            mLE.setTargetGain(800);
+            assertEquals("target gain differs from value set", 800.0f, mLE.getTargetGain());
+        } catch (IllegalArgumentException e) {
+            fail("target gain illegal argument");
+        } catch (UnsupportedOperationException e) {
+            fail("target gain unsupported operation");
+        } catch (IllegalStateException e) {
+            fail("target gain operation called in wrong state");
+        } finally {
+            releaseLoudnessEnhancer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+    private void getLoudnessEnhancer(int session) {
+        releaseLoudnessEnhancer();
+        try {
+            mLE = new LoudnessEnhancer(session);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "getLoudnessEnhancer() LoudnessEnhancer not found exception: ", e);
+        } catch (UnsupportedOperationException e) {
+            Log.e(TAG, "getLoudnessEnhancer() Effect library not loaded exception: ", e);
+        }
+        assertNotNull("could not create LoudnessEnhancer", mLE);
+    }
+
+    private void releaseLoudnessEnhancer() {
+        if (mLE != null) {
+            mLE.release();
+            mLE = null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
new file mode 100644
index 0000000..996ddf7
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.content.ComponentName;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+import android.service.media.MediaBrowserService.BrowserRoot;
+import android.test.InstrumentationTestCase;
+
+import java.util.List;
+
+/**
+ * Test {@link android.media.browse.MediaBrowserService}.
+ */
+public class MediaBrowserServiceTest extends InstrumentationTestCase {
+    // The maximum time to wait for an operation.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final long WAIT_TIME_FOR_NO_RESPONSE_MS = 500L;
+    private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+            "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
+    private final Object mWaitLock = new Object();
+    private final MediaBrowser.ConnectionCallback mConnectionCallback =
+            new MediaBrowser.ConnectionCallback() {
+                @Override
+                public void onConnected() {
+                    synchronized (mWaitLock) {
+                        mMediaBrowserService = StubMediaBrowserService.sInstance;
+                        mWaitLock.notify();
+                    }
+                }
+            };
+
+    private final MediaBrowser.SubscriptionCallback mSubscriptionCallback  =
+            new MediaBrowser.SubscriptionCallback() {
+                @Override
+                public void onChildrenLoaded(String parentId, List<MediaItem> children) {
+                    synchronized (mWaitLock) {
+                        mOnChildrenLoaded = true;
+                        mWaitLock.notify();
+                    }
+                }
+            };
+
+    private MediaBrowser mMediaBrowser;
+    private StubMediaBrowserService mMediaBrowserService;
+    private boolean mOnChildrenLoaded;
+
+    @Override
+    protected void setUp() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
+                        TEST_BROWSER_SERVICE, mConnectionCallback, null);
+            }
+        });
+        synchronized (mWaitLock) {
+            mMediaBrowser.connect();
+            mWaitLock.wait(TIME_OUT_MS);
+        }
+        assertNotNull(mMediaBrowserService);
+    }
+
+    public void testGetSessionToken() {
+        assertEquals(StubMediaBrowserService.sSession.getSessionToken(),
+                mMediaBrowserService.getSessionToken());
+    }
+
+    public void testNotifyChildrenChanged() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+
+            mOnChildrenLoaded = false;
+            mMediaBrowserService.notifyChildrenChanged(StubMediaBrowserService.MEDIA_ID_ROOT);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+        }
+    }
+
+    public void testDelayedNotifyChildrenChanged() throws Exception {
+        synchronized (mWaitLock) {
+            mOnChildrenLoaded = false;
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED,
+                    mSubscriptionCallback);
+            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertFalse(mOnChildrenLoaded);
+
+            mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+
+            mOnChildrenLoaded = false;
+            mMediaBrowserService.notifyChildrenChanged(
+                    StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED);
+            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertFalse(mOnChildrenLoaded);
+
+            mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+        }
+    }
+
+    public void testBrowserRoot() {
+        final String id = "test-id";
+        final String key = "test-key";
+        final String val = "test-val";
+        final Bundle extras = new Bundle();
+        extras.putString(key, val);
+
+        MediaBrowserService.BrowserRoot browserRoot = new BrowserRoot(id, extras);
+        assertEquals(id, browserRoot.getRootId());
+        assertEquals(val, browserRoot.getExtras().getString(key));
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
new file mode 100644
index 0000000..51d43d9
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.content.ComponentName;
+import android.cts.util.PollingCheck;
+import android.media.browse.MediaBrowser;
+import android.test.InstrumentationTestCase;
+
+import java.util.List;
+
+/**
+ * Test {@link android.media.browse.MediaBrowser}.
+ */
+public class MediaBrowserTest extends InstrumentationTestCase {
+    // The maximum time to wait for an operation.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+            "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
+    private static final ComponentName TEST_INVALID_BROWSER_SERVICE = new ComponentName(
+            "invalid.package", "invalid.ServiceClassName");
+    private final StubConnectionCallback mConnectionCallback = new StubConnectionCallback();
+    private final StubSubscriptionCallback mSubscriptionCallback = new StubSubscriptionCallback();
+
+    private MediaBrowser mMediaBrowser;
+
+    public void testMediaBrowser() {
+        resetCallbacks();
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        assertEquals(false, mMediaBrowser.isConnected());
+
+        connectMediaBrowserService();
+        assertEquals(true, mMediaBrowser.isConnected());
+
+        assertEquals(TEST_BROWSER_SERVICE, mMediaBrowser.getServiceComponent());
+        assertEquals(StubMediaBrowserService.MEDIA_ID_ROOT, mMediaBrowser.getRoot());
+        assertEquals(StubMediaBrowserService.EXTRAS_VALUE,
+                mMediaBrowser.getExtras().getString(StubMediaBrowserService.EXTRAS_KEY));
+        assertEquals(StubMediaBrowserService.sSession.getSessionToken(),
+                mMediaBrowser.getSessionToken());
+
+        mMediaBrowser.disconnect();
+        assertEquals(false, mMediaBrowser.isConnected());
+    }
+
+    public void testConnectTwice() {
+        resetCallbacks();
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        connectMediaBrowserService();
+        try {
+            mMediaBrowser.connect();
+            fail();
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    public void testConnectionFailed() {
+        resetCallbacks();
+        createMediaBrowser(TEST_INVALID_BROWSER_SERVICE);
+
+        mMediaBrowser.connect();
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return mConnectionCallback.mConnectionFailedCount > 0
+                        && mConnectionCallback.mConnectedCount == 0
+                        && mConnectionCallback.mConnectionSuspendedCount == 0;
+            }
+        }.run();
+    }
+
+    public void testGetServiceComponentBeforeConnection() {
+        resetCallbacks();
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        try {
+            ComponentName serviceComponent = mMediaBrowser.getServiceComponent();
+            fail();
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    public void testSubscribe() {
+        resetCallbacks();
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        connectMediaBrowserService();
+        mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return mSubscriptionCallback.mChildrenLoadedCount > 0;
+            }
+        }.run();
+
+        assertEquals(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback.mLastParentId);
+        assertEquals(StubMediaBrowserService.MEDIA_ID_CHILDREN.length,
+                mSubscriptionCallback.mLastChildMediaItems.size());
+        for (int i = 0; i < StubMediaBrowserService.MEDIA_ID_CHILDREN.length; i++) {
+            assertEquals(StubMediaBrowserService.MEDIA_ID_CHILDREN[i],
+                    mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+        }
+    }
+
+    private void createMediaBrowser(final ComponentName component) {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
+                        component, mConnectionCallback, null);
+            }
+        });
+    }
+
+    private void connectMediaBrowserService() {
+        mMediaBrowser.connect();
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return mConnectionCallback.mConnectedCount > 0;
+            }
+        }.run();
+    }
+
+    private void resetCallbacks() {
+        mConnectionCallback.reset();
+        mSubscriptionCallback.reset();
+    }
+
+    private static class StubConnectionCallback extends MediaBrowser.ConnectionCallback {
+        volatile int mConnectedCount;
+        volatile int mConnectionFailedCount;
+        volatile int mConnectionSuspendedCount;
+
+        public void reset() {
+            mConnectedCount = 0;
+            mConnectionFailedCount = 0;
+            mConnectionSuspendedCount = 0;
+        }
+
+        @Override
+        public void onConnected() {
+            mConnectedCount++;
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            mConnectionFailedCount++;
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            mConnectionSuspendedCount++;
+        }
+    }
+
+    private static class StubSubscriptionCallback extends MediaBrowser.SubscriptionCallback {
+        private volatile int mChildrenLoadedCount;
+        private volatile int mErrorCount;
+        private volatile String mLastErrorId;
+        private volatile String mLastParentId;
+        private volatile List<MediaBrowser.MediaItem> mLastChildMediaItems;
+
+        public void reset() {
+            mChildrenLoadedCount = 0;
+            mErrorCount = 0;
+            mLastErrorId = null;
+            mLastParentId = null;
+            mLastChildMediaItems = null;
+        }
+
+        @Override
+        public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+            mChildrenLoadedCount++;
+            mLastParentId = parentId;
+            mLastChildMediaItems = children;
+        }
+
+        @Override
+        public void onError(String id) {
+            mLastErrorId = id;
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 979a5d1..159d13f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -17,22 +17,29 @@
 
 import android.content.pm.PackageManager;
 import android.cts.util.MediaUtils;
+import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecInfo.VideoCapabilities;
 import static android.media.MediaCodecInfo.CodecProfileLevel.*;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
 import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
 import static android.media.MediaFormat.MIMETYPE_VIDEO_H263;
 import static android.media.MediaFormat.MIMETYPE_VIDEO_HEVC;
 import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
-import android.media.MediaCodecList;
-import android.media.MediaFormat;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_VP8;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_VP9;
 import android.media.MediaPlayer;
-
 import android.os.Build;
-
 import android.util.Log;
 
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Arrays;
+
 /**
  * Basic sanity test of data returned by MediaCodeCapabilities.
  */
@@ -40,6 +47,17 @@
 
     private static final String TAG = "MediaCodecCapabilitiesTest";
     private static final int PLAY_TIME_MS = 30000;
+    private static final int TIMEOUT_US = 1000000;  // 1 sec
+    private static final int IFRAME_INTERVAL = 10;          // 10 seconds between I-frames
+
+    private final MediaCodecList mRegularCodecs =
+            new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+    private final MediaCodecList mAllCodecs =
+            new MediaCodecList(MediaCodecList.ALL_CODECS);
+    private final MediaCodecInfo[] mRegularInfos =
+            mRegularCodecs.getCodecInfos();
+    private final MediaCodecInfo[] mAllInfos =
+            mAllCodecs.getCodecInfos();
 
     // Android device implementations with H.264 encoders, MUST support Baseline Profile Level 3.
     // SHOULD support Main Profile/ Level 4, if supported the device must also support Main
@@ -131,7 +149,7 @@
                     hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41));
         }
 
-        if (MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
+        if (isTv() && MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
             assertTrue(
                     "H.265 must support Main10 Profile Main Tier Level 5 if UHD is supported",
                     hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain10, HEVCMainTierLevel5));
@@ -332,4 +350,182 @@
         }
         return false;
     }
+
+    private boolean isVideoMime(String mime) {
+        return mime.toLowerCase().startsWith("video/");
+    }
+
+    private Set<String> requiredAdaptiveFormats() {
+        Set<String> adaptiveFormats = new HashSet<String>();
+        adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+        adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+        adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+        adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+        return adaptiveFormats;
+    }
+
+    public void testHaveAdaptiveVideoDecoderForAllSupportedFormats() {
+        Set<String> supportedFormats = new HashSet<String>();
+        boolean skipped = true;
+
+        // gather all supported video formats
+        for (MediaCodecInfo info : mAllInfos) {
+            if (info.isEncoder()) {
+                continue;
+            }
+            for (String mime : info.getSupportedTypes()) {
+                if (isVideoMime(mime)) {
+                    supportedFormats.add(mime);
+                }
+            }
+        }
+
+        // limit to CDD-required formats for now
+        supportedFormats.retainAll(requiredAdaptiveFormats());
+
+        // check if there is an adaptive decoder for each
+        for (String mime : supportedFormats) {
+            skipped = false;
+            // implicit assumption that QVGA video is always valid.
+            MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
+            format.setFeatureEnabled(CodecCapabilities.FEATURE_AdaptivePlayback, true);
+            String codec = mAllCodecs.findDecoderForFormat(format);
+            assertTrue(
+                    "could not find adaptive decoder for " + mime, codec != null);
+        }
+        if (skipped) {
+            MediaUtils.skipTest("no video decoders that are required to be adaptive found");
+        }
+    }
+
+    public void testAllVideoDecodersAreAdaptive() {
+        Set<String> adaptiveFormats = requiredAdaptiveFormats();
+        boolean skipped = true;
+        for (MediaCodecInfo info : mAllInfos) {
+            if (info.isEncoder()) {
+                continue;
+            }
+            for (String mime : info.getSupportedTypes()) {
+                if (!isVideoMime(mime)
+                        // limit to CDD-required formats for now
+                        || !adaptiveFormats.contains(mime)) {
+                    continue;
+                }
+                skipped = false;
+                CodecCapabilities caps = info.getCapabilitiesForType(mime);
+                assertTrue(
+                    info.getName() + " is not adaptive for " + mime,
+                    caps.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback));
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("no video decoders that are required to be adaptive found");
+        }
+    }
+
+    private MediaFormat createReasonableVideoFormat(
+            CodecCapabilities caps, String mime, boolean encoder, int width, int height) {
+        VideoCapabilities vidCaps = caps.getVideoCapabilities();
+        MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
+        if (encoder) {
+            // bitrate
+            int maxWidth = vidCaps.getSupportedWidths().getUpper();
+            int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
+            int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
+            int bitrate = vidCaps.getBitrateRange().clamp(
+                    (int)(vidCaps.getBitrateRange().getUpper()
+                            / Math.sqrt((double)maxWidth * maxHeight / width / height)));
+            Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
+                    + " " + mime + " = " + bitrate);
+            format.setInteger(format.KEY_BIT_RATE, bitrate);
+            format.setInteger(format.KEY_FRAME_RATE, maxRate);
+            format.setInteger(format.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+        }
+        return format;
+    }
+
+    public void testSecureCodecsAdvertiseSecurePlayback() throws IOException {
+        boolean skipped = true;
+        for (MediaCodecInfo info : mAllInfos) {
+            boolean isEncoder = info.isEncoder();
+            if (isEncoder || !info.getName().endsWith(".secure")) {
+                continue;
+            }
+            for (String mime : info.getSupportedTypes()) {
+                if (!isVideoMime(mime)) {
+                    continue;
+                }
+                skipped = false;
+                CodecCapabilities caps = info.getCapabilitiesForType(mime);
+                assertTrue(
+                        info.getName() + " does not advertise secure playback",
+                        caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback));
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("no video decoders found ending in .secure");
+        }
+    }
+
+    public void testAllNonTunneledVideoCodecsSupportFlexibleYUV() throws IOException {
+        boolean skipped = true;
+        for (MediaCodecInfo info : mAllInfos) {
+            boolean isEncoder = info.isEncoder();
+            for (String mime: info.getSupportedTypes()) {
+                if (!isVideoMime(mime)) {
+                    continue;
+                }
+                CodecCapabilities caps = info.getCapabilitiesForType(mime);
+                if (caps.isFeatureRequired(CodecCapabilities.FEATURE_TunneledPlayback)
+                        || caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback)) {
+                    continue;
+                }
+                skipped = false;
+                boolean found = false;
+                for (int c : caps.colorFormats) {
+                    if (c == caps.COLOR_FormatYUV420Flexible) {
+                        found = true;
+                        break;
+                    }
+                }
+                assertTrue(
+                    info.getName() + " does not advertise COLOR_FormatYUV420Flexible",
+                    found);
+
+                MediaCodec codec = null;
+                MediaFormat format = null;
+                try {
+                    codec = MediaCodec.createByCodecName(info.getName());
+                    // implicit assumption that QVGA video is always valid.
+                    format = createReasonableVideoFormat(caps, mime, isEncoder, 176, 144);
+                    format.setInteger(
+                            MediaFormat.KEY_COLOR_FORMAT,
+                            caps.COLOR_FormatYUV420Flexible);
+
+                    codec.configure(format, null /* surface */, null /* crypto */,
+                            isEncoder ? codec.CONFIGURE_FLAG_ENCODE : 0);
+                    MediaFormat configuredFormat =
+                            isEncoder ? codec.getInputFormat() : codec.getOutputFormat();
+                    Log.d(TAG, "color format is " + configuredFormat.getInteger(
+                            MediaFormat.KEY_COLOR_FORMAT));
+                    if (isEncoder) {
+                        codec.start();
+                        int ix = codec.dequeueInputBuffer(TIMEOUT_US);
+                        assertNotNull(
+                                info.getName() + " encoder has non-flexYUV input buffer #" + ix,
+                                codec.getInputImage(ix));
+                    } else {
+                        // TODO: test these on various decoders (need test streams)
+                    }
+                } finally {
+                    if (codec != null) {
+                        codec.release();
+                    }
+                }
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("no non-tunneled/non-secure video decoders found");
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
index 90696ff..b96d38c 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
@@ -15,6 +15,7 @@
  */
 package android.media.cts;
 
+import android.media.AudioManager;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
@@ -40,7 +41,7 @@
  * {@link MediaDrm} can be used to obtain keys for decrypting protected media streams,
  * in conjunction with MediaCrypto.
  */
-public class MediaCodecCencPlayer {
+public class MediaCodecCencPlayer implements MediaTimeProvider {
     private static final String TAG = MediaCodecCencPlayer.class.getSimpleName();
 
     private static final int STATE_IDLE = 1;
@@ -300,10 +301,14 @@
 
         CodecState state;
         if (isVideo) {
-            state = new CodecState(this, mVideoExtractor, trackIndex, format, codec, true);
+            state = new CodecState((MediaTimeProvider)this, mVideoExtractor,
+                            trackIndex, format, codec, true, false, 
+                            AudioManager.AUDIO_SESSION_ID_GENERATE);
             mVideoCodecStates.put(Integer.valueOf(trackIndex), state);
         } else {
-            state = new CodecState(this, mAudioExtractor, trackIndex, format, codec, true);
+            state = new CodecState((MediaTimeProvider)this, mAudioExtractor,
+                            trackIndex, format, codec, true, false,
+                            AudioManager.AUDIO_SESSION_ID_GENERATE);
             mAudioCodecStates.put(Integer.valueOf(trackIndex), state);
         }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index e04517d..9442d09 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -368,7 +368,7 @@
         // Mandatory audio encoders (for non-watch devices with camera)
 
         if (hasMicrophone() && !isWatch()) {
-            list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 16000));
+            list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 8000));
             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 48000));
             // flac encoder is not required
             // list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_FLAC, true));  // encoder
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 937eee6..494e823 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -36,7 +36,6 @@
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-
 /**
  * General MediaCodec tests.
  *
@@ -1090,4 +1089,4 @@
         }
         return false;
     }
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTunneledPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecTunneledPlayer.java
new file mode 100644
index 0000000..411cd14
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTunneledPlayer.java
@@ -0,0 +1,506 @@
+/*
+ * 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.media.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.net.Uri;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * JB(API 21) introduces {@link MediaCodec} tunneled mode API.  It allows apps
+ * to use MediaCodec to delegate their Audio/Video rendering to a vendor provided
+ * Codec component.
+ */
+public class MediaCodecTunneledPlayer implements MediaTimeProvider {
+    private static final String TAG = MediaCodecTunneledPlayer.class.getSimpleName();
+
+    private static final int STATE_IDLE = 1;
+    private static final int STATE_PREPARING = 2;
+    private static final int STATE_PLAYING = 3;
+    private static final int STATE_PAUSED = 4;
+
+    private Boolean mThreadStarted = false;
+    private byte[] mSessionId;
+    private CodecState mAudioTrackState;
+    private int mMediaFormatHeight;
+    private int mMediaFormatWidth;
+    private Integer mState;
+    private long mDeltaTimeUs;
+    private long mDurationUs;
+    private Map<Integer, CodecState> mAudioCodecStates;
+    private Map<Integer, CodecState> mVideoCodecStates;
+    private Map<String, String> mAudioHeaders;
+    private Map<String, String> mVideoHeaders;
+    private MediaExtractor mAudioExtractor;
+    private MediaExtractor mVideoExtractor;
+    private SurfaceHolder mSurfaceHolder;
+    private Thread mThread;
+    private Uri mAudioUri;
+    private Uri mVideoUri;
+    private boolean mTunneled;
+    private int mAudioSessionId;
+
+    /*
+     * Media player class to playback video using tunneled MediaCodec.
+     */
+    public MediaCodecTunneledPlayer(SurfaceHolder holder, boolean tunneled, int AudioSessionId) {
+        mSurfaceHolder = holder;
+        mTunneled = tunneled;
+        mAudioTrackState = null;
+        mState = STATE_IDLE;
+        mAudioSessionId = AudioSessionId;
+        mThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                while (true) {
+                    synchronized (mThreadStarted) {
+                        if (mThreadStarted == false) {
+                            break;
+                        }
+                    }
+                    synchronized (mState) {
+                        if (mState == STATE_PLAYING) {
+                            doSomeWork();
+                            if (mAudioTrackState != null) {
+                                mAudioTrackState.process();
+                            }
+                        }
+                    }
+                    try {
+                        Thread.sleep(5);
+                    } catch (InterruptedException ex) {
+                        Log.d(TAG, "Thread interrupted");
+                    }
+                }
+            }
+        });
+    }
+
+    public void setAudioDataSource(Uri uri, Map<String, String> headers) {
+        mAudioUri = uri;
+        mAudioHeaders = headers;
+    }
+
+    public void setVideoDataSource(Uri uri, Map<String, String> headers) {
+        mVideoUri = uri;
+        mVideoHeaders = headers;
+    }
+
+    public final int getMediaFormatHeight() {
+        return mMediaFormatHeight;
+    }
+
+    public final int getMediaFormatWidth() {
+        return mMediaFormatWidth;
+    }
+
+    private boolean prepareAudio() throws IOException {
+        for (int i = mAudioExtractor.getTrackCount(); i-- > 0;) {
+            MediaFormat format = mAudioExtractor.getTrackFormat(i);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+
+            if (!mime.startsWith("audio/")) {
+                continue;
+            }
+
+            Log.d(TAG, "audio track #" + i + " " + format + " " + mime +
+                  " Is ADTS:" + getMediaFormatInteger(format, MediaFormat.KEY_IS_ADTS) +
+                  " Sample rate:" + getMediaFormatInteger(format, MediaFormat.KEY_SAMPLE_RATE) +
+                  " Channel count:" +
+                  getMediaFormatInteger(format, MediaFormat.KEY_CHANNEL_COUNT));
+
+            mAudioExtractor.selectTrack(i);
+            if (!addTrack(i, format)) {
+                Log.e(TAG, "prepareAudio - addTrack() failed!");
+                return false;
+            }
+
+            if (format.containsKey(MediaFormat.KEY_DURATION)) {
+                long durationUs = format.getLong(MediaFormat.KEY_DURATION);
+
+                if (durationUs > mDurationUs) {
+                    mDurationUs = durationUs;
+                }
+                Log.d(TAG, "audio track format #" + i +
+                        " Duration:" + mDurationUs + " microseconds");
+            }
+        }
+        return true;
+    }
+
+    private boolean prepareVideo() throws IOException {
+        for (int i = mVideoExtractor.getTrackCount(); i-- > 0;) {
+            MediaFormat format = mVideoExtractor.getTrackFormat(i);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+
+            if (!mime.startsWith("video/")) {
+                continue;
+            }
+
+            mMediaFormatHeight = getMediaFormatInteger(format, MediaFormat.KEY_HEIGHT);
+            mMediaFormatWidth = getMediaFormatInteger(format, MediaFormat.KEY_WIDTH);
+            Log.d(TAG, "video track #" + i + " " + format + " " + mime +
+                  " Width:" + mMediaFormatWidth + ", Height:" + mMediaFormatHeight);
+
+            mVideoExtractor.selectTrack(i);
+            if (!addTrack(i, format)) {
+                Log.e(TAG, "prepareVideo - addTrack() failed!");
+                return false;
+            }
+
+            if (format.containsKey(MediaFormat.KEY_DURATION)) {
+                long durationUs = format.getLong(MediaFormat.KEY_DURATION);
+
+                if (durationUs > mDurationUs) {
+                    mDurationUs = durationUs;
+                }
+                Log.d(TAG, "track format #" + i + " Duration:" +
+                        mDurationUs + " microseconds");
+            }
+        }
+        return true;
+    }
+
+    public boolean prepare() throws IOException {
+        if (null == mAudioExtractor) {
+            mAudioExtractor = new MediaExtractor();
+            if (null == mAudioExtractor) {
+                Log.e(TAG, "prepare - Cannot create Audio extractor.");
+                return false;
+            }
+        }
+
+        if (null == mVideoExtractor){
+            mVideoExtractor = new MediaExtractor();
+            if (null == mVideoExtractor) {
+                Log.e(TAG, "prepare - Cannot create Video extractor.");
+                return false;
+            }
+        }
+
+        mAudioExtractor.setDataSource(mAudioUri.toString(), mAudioHeaders);
+        mVideoExtractor.setDataSource(mVideoUri.toString(), mVideoHeaders);
+
+        if (null == mVideoCodecStates) {
+            mVideoCodecStates = new HashMap<Integer, CodecState>();
+        } else {
+            mVideoCodecStates.clear();
+        }
+
+        if (null == mAudioCodecStates) {
+            mAudioCodecStates = new HashMap<Integer, CodecState>();
+        } else {
+            mAudioCodecStates.clear();
+        }
+
+        if (!prepareAudio()) {
+            Log.e(TAG,"prepare - prepareAudio() failed!");
+            return false;
+        }
+        if (!prepareVideo()) {
+            Log.e(TAG,"prepare - prepareVideo() failed!");
+            return false;
+        }
+
+        synchronized (mState) {
+            mState = STATE_PAUSED;
+        }
+        return true;
+    }
+
+    private boolean addTrack(int trackIndex, MediaFormat format) throws IOException {
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        boolean isVideo = mime.startsWith("video/");
+        boolean isAudio = mime.startsWith("audio/");
+        MediaCodec codec;
+
+        // setup tunneled video codec if needed
+        if (isVideo && mTunneled) {
+            format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback,
+                        true);
+            MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+            String codecName = mcl.findDecoderForFormat(format);
+            if (codecName == null) {
+                Log.e(TAG,"addTrack - Could not find Tunneled playback codec for "+mime+
+                        " format!");
+                return false;
+            }
+
+            codec = MediaCodec.createByCodecName(codecName);
+            if (codec == null) {
+                Log.e(TAG, "addTrack - Could not create Tunneled playback codec "+
+                        codecName+"!");
+                return false;
+            }
+
+            if (mAudioTrackState != null) {
+                format.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, mAudioSessionId);
+            }
+        }
+        else {
+            codec = MediaCodec.createDecoderByType(mime);
+            if (codec == null) {
+                Log.e(TAG, "addTrack - Could not create regular playback codec for mime "+
+                        mime+"!");
+                return false;
+            }
+        }
+        codec.configure(
+                format,
+                isVideo ? mSurfaceHolder.getSurface() : null, null, 0);
+
+        CodecState state;
+        if (isVideo) {
+            state = new CodecState((MediaTimeProvider)this, mVideoExtractor,
+                            trackIndex, format, codec, true, mTunneled, mAudioSessionId);
+            mVideoCodecStates.put(Integer.valueOf(trackIndex), state);
+        } else {
+            state = new CodecState((MediaTimeProvider)this, mAudioExtractor,
+                            trackIndex, format, codec, true, mTunneled, mAudioSessionId);
+            mAudioCodecStates.put(Integer.valueOf(trackIndex), state);
+        }
+
+        if (isAudio) {
+            mAudioTrackState = state;
+        }
+
+        return true;
+    }
+
+    protected int getMediaFormatInteger(MediaFormat format, String key) {
+        return format.containsKey(key) ? format.getInteger(key) : 0;
+    }
+
+    public boolean start() {
+        Log.d(TAG, "start");
+
+        synchronized (mState) {
+            if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
+                return true;
+            } else if (mState == STATE_IDLE) {
+                mState = STATE_PREPARING;
+                return true;
+            } else if (mState != STATE_PAUSED) {
+                throw new IllegalStateException();
+            }
+
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.start();
+            }
+
+            for (CodecState state : mAudioCodecStates.values()) {
+                state.start();
+            }
+
+            mDeltaTimeUs = -1;
+            mState = STATE_PLAYING;
+        }
+        return false;
+    }
+
+    public void startWork() throws IOException, Exception {
+        try {
+            // Just change state from STATE_IDLE to STATE_PREPARING.
+            start();
+            // Extract media information from uri asset, and change state to STATE_PAUSED.
+            prepare();
+            // Start CodecState, and change from STATE_PAUSED to STATE_PLAYING.
+            start();
+        } catch (IOException e) {
+            throw e;
+        }
+
+        synchronized (mThreadStarted) {
+            mThreadStarted = true;
+            mThread.start();
+        }
+    }
+
+    public void startThread() {
+        start();
+        synchronized (mThreadStarted) {
+            mThreadStarted = true;
+            mThread.start();
+        }
+    }
+
+    public void pause() {
+        Log.d(TAG, "pause");
+
+        synchronized (mState) {
+            if (mState == STATE_PAUSED) {
+                return;
+            } else if (mState != STATE_PLAYING) {
+                throw new IllegalStateException();
+            }
+
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.pause();
+            }
+
+            for (CodecState state : mAudioCodecStates.values()) {
+                state.pause();
+            }
+
+            mState = STATE_PAUSED;
+        }
+    }
+
+    public void flush() {
+        Log.d(TAG, "flush");
+
+        synchronized (mState) {
+            if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
+                return;
+            }
+
+            for (CodecState state : mAudioCodecStates.values()) {
+                state.flush();
+            }
+
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.flush();
+            }
+        }
+    }
+
+    public void reset() {
+        synchronized (mState) {
+            if (mState == STATE_PLAYING) {
+                pause();
+            }
+            if (mVideoCodecStates != null) {
+                for (CodecState state : mVideoCodecStates.values()) {
+                    state.release();
+                }
+                mVideoCodecStates = null;
+            }
+
+            if (mAudioCodecStates != null) {
+                for (CodecState state : mAudioCodecStates.values()) {
+                    state.release();
+                }
+                mAudioCodecStates = null;
+            }
+
+            if (mAudioExtractor != null) {
+                mAudioExtractor.release();
+                mAudioExtractor = null;
+            }
+
+            if (mVideoExtractor != null) {
+                mVideoExtractor.release();
+                mVideoExtractor = null;
+            }
+
+            mDurationUs = -1;
+            mState = STATE_IDLE;
+        }
+        synchronized (mThreadStarted) {
+            mThreadStarted = false;
+        }
+        try {
+            mThread.join();
+        } catch (InterruptedException ex) {
+            Log.d(TAG, "mThread.join " + ex);
+        }
+    }
+
+    public boolean isEnded() {
+        for (CodecState state : mVideoCodecStates.values()) {
+          if (!state.isEnded()) {
+            return false;
+          }
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            if (!state.isEnded()) {
+              return false;
+            }
+        }
+
+        return true;
+    }
+
+    private void doSomeWork() {
+        try {
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.doSomeWork();
+            }
+        } catch (IllegalStateException e) {
+            throw new Error("Video CodecState.doSomeWork" + e);
+        }
+
+        try {
+            for (CodecState state : mAudioCodecStates.values()) {
+                state.doSomeWork();
+            }
+        } catch (IllegalStateException e) {
+            throw new Error("Audio CodecState.doSomeWork" + e);
+        }
+
+    }
+
+    public long getNowUs() {
+        if (mAudioTrackState == null) {
+            return System.currentTimeMillis() * 1000;
+        }
+
+        return mAudioTrackState.getAudioTimeUs();
+    }
+
+    public long getRealTimeUsForMediaTime(long mediaTimeUs) {
+        if (mDeltaTimeUs == -1) {
+            long nowUs = getNowUs();
+            mDeltaTimeUs = nowUs - mediaTimeUs;
+        }
+
+        return mDeltaTimeUs + mediaTimeUs;
+    }
+
+    public int getDuration() {
+        return (int)((mDurationUs + 500) / 1000);
+    }
+
+    public int getCurrentPosition() {
+        if (mVideoCodecStates == null) {
+                return 0;
+        }
+
+        long positionUs = 0;
+
+        for (CodecState state : mVideoCodecStates.values()) {
+            long trackPositionUs = state.getCurrentPositionUs();
+
+            if (trackPositionUs > positionUs) {
+                positionUs = trackPositionUs;
+            }
+        }
+        return (int)((positionUs + 500) / 1000);
+    }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
new file mode 100644
index 0000000..a153c4d
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.media.AudioManager;
+import android.media.Rating;
+import android.media.VolumeProvider;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState.CustomAction;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.test.AndroidTestCase;
+
+/**
+ * Test {@link android.media.session.MediaController}.
+ */
+public class MediaControllerTest extends AndroidTestCase {
+    // The maximum time to wait for an operation.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final String SESSION_TAG = "test-session";
+    private static final String EXTRAS_KEY = "test-key";
+    private static final String EXTRAS_VALUE = "test-val";
+
+    private final Object mWaitLock = new Object();
+    private Handler mHandler = new Handler(Looper.getMainLooper());
+    private MediaSession mSession;
+    private MediaSessionCallback mCallback = new MediaSessionCallback();
+    private MediaController mController;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSession = new MediaSession(getContext(), SESSION_TAG);
+        mSession.setCallback(mCallback, mHandler);
+        mController = mSession.getController();
+    }
+
+    public void testSendCommand() throws Exception {
+        synchronized (mWaitLock) {
+            mCallback.reset();
+            final String command = "test-command";
+            final Bundle extras = new Bundle();
+            extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+            mController.sendCommand(command, extras, new ResultReceiver(null));
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnCommandCalled);
+            assertNotNull(mCallback.mCommandCallback);
+            assertEquals(command, mCallback.mCommand);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+        }
+    }
+
+    public void testVolumeControl() throws Exception {
+        VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_ABSOLUTE, 11, 5) {
+            @Override
+            public void onSetVolumeTo(int volume) {
+                synchronized (mWaitLock) {
+                    setCurrentVolume(volume);
+                    mWaitLock.notify();
+                }
+            }
+
+            @Override
+            public void onAdjustVolume(int direction) {
+                synchronized (mWaitLock) {
+                    switch (direction) {
+                        case AudioManager.ADJUST_LOWER:
+                            setCurrentVolume(getCurrentVolume() - 1);
+                            break;
+                        case AudioManager.ADJUST_RAISE:
+                            setCurrentVolume(getCurrentVolume() + 1);
+                            break;
+                    }
+                    mWaitLock.notify();
+                }
+            }
+        };
+        mSession.setPlaybackToRemote(vp);
+
+        synchronized (mWaitLock) {
+            // test setVolumeTo
+            mController.setVolumeTo(7, 0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(7, vp.getCurrentVolume());
+
+            // test adjustVolume
+            mController.adjustVolume(AudioManager.ADJUST_LOWER, 0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(6, vp.getCurrentVolume());
+
+            mController.adjustVolume(AudioManager.ADJUST_RAISE, 0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(7, vp.getCurrentVolume());
+        }
+    }
+
+    public void testTransportControlsAndMediaSessionCallback() throws Exception {
+        MediaController.TransportControls controls = mController.getTransportControls();
+        synchronized (mWaitLock) {
+            mCallback.reset();
+            controls.play();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlayCalled);
+
+            mCallback.reset();
+            controls.pause();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPauseCalled);
+
+            mCallback.reset();
+            controls.stop();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnStopCalled);
+
+            mCallback.reset();
+            controls.fastForward();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnFastForwardCalled);
+
+            mCallback.reset();
+            controls.rewind();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnRewindCalled);
+
+            mCallback.reset();
+            controls.skipToPrevious();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSkipToPreviousCalled);
+
+            mCallback.reset();
+            controls.skipToNext();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSkipToNextCalled);
+
+            mCallback.reset();
+            final long seekPosition = 1000;
+            controls.seekTo(seekPosition);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSeekToCalled);
+            assertEquals(seekPosition, mCallback.mSeekPosition);
+
+            mCallback.reset();
+            final Rating rating = Rating.newStarRating(Rating.RATING_5_STARS, 3f);
+            controls.setRating(rating);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSetRatingCalled);
+            assertEquals(rating.getRatingStyle(), mCallback.mRating.getRatingStyle());
+            assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating());
+
+            mCallback.reset();
+            final String mediaId = "test-media-id";
+            final Bundle extras = new Bundle();
+            extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+            controls.playFromMediaId(mediaId, extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlayFromMediaIdCalled);
+            assertEquals(mediaId, mCallback.mMediaId);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+            mCallback.reset();
+            final String query = "test-query";
+            controls.playFromSearch(query, extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlayFromSearchCalled);
+            assertEquals(query, mCallback.mQuery);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+            mCallback.reset();
+            final String action = "test-action";
+            controls.sendCustomAction(action, extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnCustomActionCalled);
+            assertEquals(action, mCallback.mAction);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+            mCallback.reset();
+            mCallback.mOnCustomActionCalled = false;
+            final CustomAction customAction =
+                    new CustomAction.Builder(action, action, -1).setExtras(extras).build();
+            controls.sendCustomAction(customAction, extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnCustomActionCalled);
+            assertEquals(action, mCallback.mAction);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+            mCallback.reset();
+            final long queueItemId = 1000;
+            controls.skipToQueueItem(queueItemId);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSkipToQueueItemCalled);
+            assertEquals(queueItemId, mCallback.mQueueItemId);
+        }
+    }
+
+    private class MediaSessionCallback extends MediaSession.Callback {
+        private volatile long mSeekPosition;
+        private volatile long mQueueItemId;
+        private volatile Rating mRating;
+        private volatile String mMediaId;
+        private volatile String mQuery;
+        private volatile String mAction;
+        private volatile String mCommand;
+        private volatile Bundle mExtras;
+        private volatile ResultReceiver mCommandCallback;
+
+        private volatile boolean mOnPlayCalled;
+        private volatile boolean mOnPauseCalled;
+        private volatile boolean mOnStopCalled;
+        private volatile boolean mOnFastForwardCalled;
+        private volatile boolean mOnRewindCalled;
+        private volatile boolean mOnSkipToPreviousCalled;
+        private volatile boolean mOnSkipToNextCalled;
+        private volatile boolean mOnSeekToCalled;
+        private volatile boolean mOnSkipToQueueItemCalled;
+        private volatile boolean mOnSetRatingCalled;
+        private volatile boolean mOnPlayFromMediaIdCalled;
+        private volatile boolean mOnPlayFromSearchCalled;
+        private volatile boolean mOnCustomActionCalled;
+        private volatile boolean mOnCommandCalled;
+
+        public void reset() {
+            mSeekPosition = -1;
+            mQueueItemId = -1;
+            mRating = null;
+            mMediaId = null;
+            mQuery = null;
+            mAction = null;
+            mExtras = null;
+            mCommand = null;
+            mCommandCallback = null;
+
+            mOnPlayCalled = false;
+            mOnPauseCalled = false;
+            mOnStopCalled = false;
+            mOnFastForwardCalled = false;
+            mOnRewindCalled = false;
+            mOnSkipToPreviousCalled = false;
+            mOnSkipToNextCalled = false;
+            mOnSkipToQueueItemCalled = false;
+            mOnSeekToCalled = false;
+            mOnSetRatingCalled = false;
+            mOnPlayFromMediaIdCalled = false;
+            mOnPlayFromSearchCalled = false;
+            mOnCustomActionCalled = false;
+            mOnCommandCalled = false;
+        }
+
+        @Override
+        public void onPlay() {
+            synchronized (mWaitLock) {
+                mOnPlayCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onPause() {
+            synchronized (mWaitLock) {
+                mOnPauseCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onStop() {
+            synchronized (mWaitLock) {
+                mOnStopCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onFastForward() {
+            synchronized (mWaitLock) {
+                mOnFastForwardCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onRewind() {
+            synchronized (mWaitLock) {
+                mOnRewindCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSkipToPrevious() {
+            synchronized (mWaitLock) {
+                mOnSkipToPreviousCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSkipToNext() {
+            synchronized (mWaitLock) {
+                mOnSkipToNextCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSeekTo(long pos) {
+            synchronized (mWaitLock) {
+                mOnSeekToCalled = true;
+                mSeekPosition = pos;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSetRating(Rating rating) {
+            synchronized (mWaitLock) {
+                mOnSetRatingCalled = true;
+                mRating = rating;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnPlayFromMediaIdCalled = true;
+                mMediaId = mediaId;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onPlayFromSearch(String query, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnPlayFromSearchCalled = true;
+                mQuery = query;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onCustomAction(String action, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnCustomActionCalled= true;
+                mAction = action;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSkipToQueueItem(long id) {
+            synchronized (mWaitLock) {
+                mOnSkipToQueueItemCalled = true;
+                mQueueItemId = id;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+            synchronized (mWaitLock) {
+                mOnCommandCalled = true;
+                mCommand = command;
+                mExtras = extras;
+                mCommandCallback = cb;
+                mWaitLock.notify();
+            }
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaItemTest.java b/tests/tests/media/src/android/media/cts/MediaItemTest.java
new file mode 100644
index 0000000..4eefaa7
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaItemTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.media.MediaDescription;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+/**
+ * Test {@link android.media.browse.MediaBrowser.MediaItem}.
+ */
+public class MediaItemTest extends AndroidTestCase {
+    private static final String DESCRIPTION = "test_description";
+    private static final String MEDIA_ID = "test_media_id";
+    private static final String TITLE = "test_title";
+    private static final String SUBTITLE = "test_subtitle";
+
+    public void testBrowsableMediaItem() {
+        MediaDescription description = new MediaDescription.Builder()
+                .setDescription(DESCRIPTION).setMediaId(MEDIA_ID)
+                .setTitle(TITLE).setSubtitle(SUBTITLE).build();
+        MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_BROWSABLE);
+
+        assertEquals(description.toString(), mediaItem.getDescription().toString());
+        assertEquals(MEDIA_ID, mediaItem.getMediaId());
+        assertEquals(MediaItem.FLAG_BROWSABLE, mediaItem.getFlags());
+        assertTrue(mediaItem.isBrowsable());
+        assertFalse(mediaItem.isPlayable());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        mediaItem.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        assertEquals(mediaItem.getFlags(), p.readInt());
+        assertEquals(description.toString(),
+                MediaDescription.CREATOR.createFromParcel(p).toString());
+        p.recycle();
+    }
+
+    public void testPlayableMediaItem() {
+        MediaDescription description = new MediaDescription.Builder()
+                .setDescription(DESCRIPTION).setMediaId(MEDIA_ID)
+                .setTitle(TITLE).setSubtitle(SUBTITLE).build();
+        MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_PLAYABLE);
+
+        assertEquals(description.toString(), mediaItem.getDescription().toString());
+        assertEquals(MEDIA_ID, mediaItem.getMediaId());
+        assertEquals(MediaItem.FLAG_PLAYABLE, mediaItem.getFlags());
+        assertFalse(mediaItem.isBrowsable());
+        assertTrue(mediaItem.isPlayable());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        mediaItem.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        assertEquals(mediaItem.getFlags(), p.readInt());
+        assertEquals(description.toString(),
+                MediaDescription.CREATOR.createFromParcel(p).toString());
+        p.recycle();
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 78b5cfd..216e21b 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -159,13 +159,29 @@
     }
 
     public void testRecorderCamera() throws Exception {
+        int width;
+        int height;
+        Camera camera = null;
         if (!hasCamera()) {
             return;
         }
+        // Try to get camera first supported resolution.
+        // If we fail for any reason, set the video size to default value.
+        try {
+            camera = Camera.open();
+            width = camera.getParameters().getSupportedPreviewSizes().get(0).width;
+            height = camera.getParameters().getSupportedPreviewSizes().get(0).height;
+        } catch (Exception e) {
+            width = VIDEO_WIDTH;
+            height = VIDEO_HEIGHT;
+        }
+        if (camera != null) {
+            camera.release();
+        }
         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
-        mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
+        mMediaRecorder.setVideoSize(width, height);
         mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE_IN_BPS);
         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
         mMediaRecorder.prepare();
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index df68392..f4a30e8 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -26,21 +26,47 @@
 import android.media.VolumeProvider;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
 import android.test.AndroidTestCase;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 public class MediaSessionTest extends AndroidTestCase {
+    // The maximum time to wait for an operation.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
+    private static final String TEST_SESSION_TAG = "test-session-tag";
+    private static final String TEST_KEY = "test-key";
+    private static final String TEST_VALUE = "test-val";
+    private static final int TEST_CURRENT_VOLUME = 10;
+    private static final int TEST_MAX_VOLUME = 11;
+    private static final long TEST_QUEUE_ID = 12L;
+    private static final long TEST_ACTION = 55L;
+
     private AudioManager mAudioManager;
+    private Handler mHandler = new Handler(Looper.getMainLooper());
+    private Object mWaitLock = new Object();
+    private MediaControllerCallback mCallback = new MediaControllerCallback();
+    private MediaSession mSession;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mSession = new MediaSession(getContext(), TEST_SESSION_TAG);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // It is OK to call release() twice.
+        mSession.release();
+        super.tearDown();
     }
 
     /**
@@ -48,15 +74,31 @@
      * initialized correctly.
      */
     public void testCreateSession() throws Exception {
-        String tag = "test session";
-        MediaSession session = new MediaSession(getContext(), tag);
-        assertNotNull(session.getSessionToken());
-        assertFalse("New session should not be active", session.isActive());
+        assertNotNull(mSession.getSessionToken());
+        assertFalse("New session should not be active", mSession.isActive());
 
         // Verify by getting the controller and checking all its fields
-        MediaController controller = session.getController();
+        MediaController controller = mSession.getController();
         assertNotNull(controller);
-        verifyNewSession(controller, tag);
+        verifyNewSession(controller, TEST_SESSION_TAG);
+    }
+
+    /**
+     * Tests MediaSession.Token created in the constructor of MediaSession.
+     */
+    public void testSessionToken() throws Exception {
+        MediaSession.Token sessionToken = mSession.getSessionToken();
+
+        assertNotNull(sessionToken);
+        assertEquals(0, sessionToken.describeContents());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        sessionToken.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        MediaSession.Token token = MediaSession.Token.CREATOR.createFromParcel(p);
+        assertEquals(token, sessionToken);
+        p.recycle();
     }
 
     /**
@@ -64,92 +106,179 @@
      * controller.
      */
     public void testConfigureSession() throws Exception {
-        String tag = "test session";
-        String key = "test-key";
-        String val = "test-val";
-        MediaSession session = new MediaSession(getContext(), tag);
-        MediaController controller = session.getController();
+        MediaController controller = mSession.getController();
+        controller.registerCallback(mCallback, mHandler);
 
-        // test setExtras
-        Bundle extras = new Bundle();
-        extras.putString(key, val);
-        session.setExtras(extras);
-        Bundle extrasOut = controller.getExtras();
-        assertNotNull(extrasOut);
-        assertEquals(val, extrasOut.get(key));
+        synchronized (mWaitLock) {
+            // test setExtras
+            mCallback.resetLocked();
+            final Bundle extras = new Bundle();
+            extras.putString(TEST_KEY, TEST_VALUE);
+            mSession.setExtras(extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnExtraChangedCalled);
 
-        // test setFlags
-        session.setFlags(5);
-        assertEquals(5, controller.getFlags());
+            Bundle extrasOut = mCallback.mExtras;
+            assertNotNull(extrasOut);
+            assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
 
-        // test setMetadata
-        MediaMetadata metadata = new MediaMetadata.Builder().putString(key, val).build();
-        session.setMetadata(metadata);
-        MediaMetadata metadataOut = controller.getMetadata();
-        assertNotNull(metadataOut);
-        assertEquals(val, metadataOut.getString(key));
+            extrasOut = controller.getExtras();
+            assertNotNull(extrasOut);
+            assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
 
-        // test setPlaybackState
-        PlaybackState state = new PlaybackState.Builder().setActions(55).build();
-        session.setPlaybackState(state);
-        PlaybackState stateOut = controller.getPlaybackState();
-        assertNotNull(stateOut);
-        assertEquals(55L, stateOut.getActions());
+            // test setFlags
+            mSession.setFlags(5);
+            assertEquals(5, controller.getFlags());
 
-        // test setPlaybackToRemote, do this before testing setPlaybackToLocal
-        // to ensure it switches correctly.
-        try {
-            session.setPlaybackToRemote(null);
-            fail("Expected IAE for setPlaybackToRemote(null)");
-        } catch (IllegalArgumentException e) {
-            // expected
+            // test setMetadata
+            mCallback.resetLocked();
+            MediaMetadata metadata =
+                    new MediaMetadata.Builder().putString(TEST_KEY, TEST_VALUE).build();
+            mSession.setMetadata(metadata);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnMetadataChangedCalled);
+
+            MediaMetadata metadataOut = mCallback.mMediaMetadata;
+            assertNotNull(metadataOut);
+            assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+            metadataOut = controller.getMetadata();
+            assertNotNull(metadataOut);
+            assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+            // test setPlaybackState
+            mCallback.resetLocked();
+            PlaybackState state = new PlaybackState.Builder().setActions(TEST_ACTION).build();
+            mSession.setPlaybackState(state);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlaybackStateChangedCalled);
+
+            PlaybackState stateOut = mCallback.mPlaybackState;
+            assertNotNull(stateOut);
+            assertEquals(TEST_ACTION, stateOut.getActions());
+
+            stateOut = controller.getPlaybackState();
+            assertNotNull(stateOut);
+            assertEquals(TEST_ACTION, stateOut.getActions());
+
+            // test setQueue and setQueueTitle
+            mCallback.resetLocked();
+            List<MediaSession.QueueItem> queue = new ArrayList<MediaSession.QueueItem>();
+            MediaSession.QueueItem item = new MediaSession.QueueItem(new MediaDescription.Builder()
+                    .setMediaId(TEST_VALUE).setTitle("title").build(), TEST_QUEUE_ID);
+            queue.add(item);
+            mSession.setQueue(queue);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnQueueChangedCalled);
+
+            mSession.setQueueTitle(TEST_VALUE);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnQueueTitleChangedCalled);
+
+            assertEquals(TEST_VALUE, mCallback.mTitle);
+            assertEquals(queue.size(), mCallback.mQueue.size());
+            assertEquals(TEST_QUEUE_ID, mCallback.mQueue.get(0).getQueueId());
+            assertEquals(TEST_VALUE, mCallback.mQueue.get(0).getDescription().getMediaId());
+
+            assertEquals(TEST_VALUE, controller.getQueueTitle());
+            assertEquals(queue.size(), controller.getQueue().size());
+            assertEquals(TEST_QUEUE_ID, controller.getQueue().get(0).getQueueId());
+            assertEquals(TEST_VALUE, controller.getQueue().get(0).getDescription().getMediaId());
+
+            mCallback.resetLocked();
+            mSession.setQueue(null);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnQueueChangedCalled);
+
+            mSession.setQueueTitle(null);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnQueueTitleChangedCalled);
+
+            assertNull(mCallback.mTitle);
+            assertNull(mCallback.mQueue);
+            assertNull(controller.getQueueTitle());
+            assertNull(controller.getQueue());
+
+            // test setSessionActivity
+            Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
+            PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
+            mSession.setSessionActivity(pi);
+            assertEquals(pi, controller.getSessionActivity());
+
+            // test setActivity
+            mSession.setActive(true);
+            assertTrue(mSession.isActive());
+
+            // test release
+            mCallback.resetLocked();
+            mSession.release();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSessionDestroyedCalled);
         }
-        VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED, 11, 11) {};
-        session.setPlaybackToRemote(vp);
-        MediaController.PlaybackInfo info = controller.getPlaybackInfo();
-        assertNotNull(info);
-        assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
-        assertEquals(11, info.getMaxVolume());
-        assertEquals(11, info.getCurrentVolume());
-        assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
-
-        // test setPlaybackToLocal
-        AudioAttributes attrs = new AudioAttributes.Builder().addTag(val).build();
-        session.setPlaybackToLocal(attrs);
-        info = controller.getPlaybackInfo();
-        assertNotNull(info);
-        assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
-        Set<String> tags = info.getAudioAttributes().getTags();
-        assertNotNull(tags);
-        assertTrue(tags.contains(val));
-
-        // test setQueue and setQueueTitle
-        ArrayList<MediaSession.QueueItem> queue = new ArrayList<MediaSession.QueueItem>();
-        MediaSession.QueueItem item = new MediaSession.QueueItem(new MediaDescription.Builder()
-                .setMediaId(val).setTitle("title").build(), 11);
-        queue.add(item);
-        session.setQueue(queue);
-        session.setQueueTitle(val);
-
-        assertEquals(val, controller.getQueueTitle());
-        assertEquals(1, controller.getQueue().size());
-        assertEquals(11, controller.getQueue().get(0).getQueueId());
-        assertEquals(val, controller.getQueue().get(0).getDescription().getMediaId());
-
-        session.setQueue(null);
-        session.setQueueTitle(null);
-
-        assertNull(controller.getQueueTitle());
-        assertNull(controller.getQueue());
-
-        // test setSessionActivity
-        Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
-        PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
-        session.setSessionActivity(pi);
-        assertEquals(pi, controller.getSessionActivity());
     }
 
     /**
+     * Tests for setPlaybackToLocal and setPlaybackToRemote.
+     */
+    public void testPlaybackToLocalAndRemote() throws Exception {
+        MediaController controller = mSession.getController();
+        controller.registerCallback(mCallback, mHandler);
+
+        synchronized (mWaitLock) {
+            // test setPlaybackToRemote, do this before testing setPlaybackToLocal
+            // to ensure it switches correctly.
+            mCallback.resetLocked();
+            try {
+                mSession.setPlaybackToRemote(null);
+                fail("Expected IAE for setPlaybackToRemote(null)");
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+            VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED,
+                    TEST_MAX_VOLUME, TEST_CURRENT_VOLUME) {};
+            mSession.setPlaybackToRemote(vp);
+
+            MediaController.PlaybackInfo info = null;
+            for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) {
+                mCallback.mOnAudioInfoChangedCalled = false;
+                mWaitLock.wait(TIME_OUT_MS);
+                assertTrue(mCallback.mOnAudioInfoChangedCalled);
+                info = mCallback.mPlaybackInfo;
+                if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME
+                        && info.getMaxVolume() == TEST_MAX_VOLUME
+                        && info.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE
+                        && info.getVolumeControl() == VolumeProvider.VOLUME_CONTROL_FIXED) {
+                    break;
+                }
+            }
+            assertNotNull(info);
+            assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+            assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+            assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+            assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
+
+            info = controller.getPlaybackInfo();
+            assertNotNull(info);
+            assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+            assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+            assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+            assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
+
+            // test setPlaybackToLocal
+            AudioAttributes attrs = new AudioAttributes.Builder().addTag(TEST_VALUE).build();
+            mSession.setPlaybackToLocal(attrs);
+
+            info = controller.getPlaybackInfo();
+            assertNotNull(info);
+            assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
+            Set<String> tags = info.getAudioAttributes().getTags();
+            assertNotNull(tags);
+            assertTrue(tags.contains(TEST_VALUE));
+        }
+    }
+
+
+    /**
      * Verifies that a new session hasn't had any configuration bits set yet.
      *
      * @param controller The controller for the session
@@ -180,4 +309,100 @@
         assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC),
                 info.getCurrentVolume());
     }
+
+    private class MediaControllerCallback extends MediaController.Callback {
+        private volatile boolean mOnPlaybackStateChangedCalled;
+        private volatile boolean mOnMetadataChangedCalled;
+        private volatile boolean mOnQueueChangedCalled;
+        private volatile boolean mOnQueueTitleChangedCalled;
+        private volatile boolean mOnExtraChangedCalled;
+        private volatile boolean mOnAudioInfoChangedCalled;
+        private volatile boolean mOnSessionDestroyedCalled;
+
+        private volatile PlaybackState mPlaybackState;
+        private volatile MediaMetadata mMediaMetadata;
+        private volatile List<MediaSession.QueueItem> mQueue;
+        private volatile CharSequence mTitle;
+        private volatile Bundle mExtras;
+        private volatile MediaController.PlaybackInfo mPlaybackInfo;
+
+        public void resetLocked() {
+            mOnPlaybackStateChangedCalled = false;
+            mOnMetadataChangedCalled = false;
+            mOnQueueChangedCalled = false;
+            mOnQueueTitleChangedCalled = false;
+            mOnExtraChangedCalled = false;
+            mOnAudioInfoChangedCalled = false;
+            mOnSessionDestroyedCalled = false;
+
+            mPlaybackState = null;
+            mMediaMetadata = null;
+            mQueue = null;
+            mTitle = null;
+            mExtras = null;
+            mPlaybackInfo = null;
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            synchronized (mWaitLock) {
+                mOnPlaybackStateChangedCalled = true;
+                mPlaybackState = state;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            synchronized (mWaitLock) {
+                mOnMetadataChangedCalled = true;
+                mMediaMetadata = metadata;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onQueueChanged(List<MediaSession.QueueItem> queue) {
+            synchronized (mWaitLock) {
+                mOnQueueChangedCalled = true;
+                mQueue = queue;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onQueueTitleChanged(CharSequence title) {
+            synchronized (mWaitLock) {
+                mOnQueueTitleChangedCalled = true;
+                mTitle = title;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onExtrasChanged(Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnExtraChangedCalled = true;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onAudioInfoChanged(MediaController.PlaybackInfo info) {
+            synchronized (mWaitLock) {
+                mOnAudioInfoChangedCalled = true;
+                mPlaybackInfo = info;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSessionDestroyed() {
+            synchronized (mWaitLock) {
+                mOnSessionDestroyedCalled = true;
+                mWaitLock.notify();
+            }
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaTimeProvider.java b/tests/tests/media/src/android/media/cts/MediaTimeProvider.java
new file mode 100644
index 0000000..4f6837e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaTimeProvider.java
@@ -0,0 +1,24 @@
+/*
+ * 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.media.cts;
+
+/*
+ * Interface used by CodecState to retrieve Media timing info from parent Player
+ */
+public interface MediaTimeProvider {
+    public long getNowUs();
+    public long getRealTimeUsForMediaTime(long mediaTimeUs);
+}
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index fea78c9..e7b08e9 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -91,7 +91,7 @@
         testExtractor(R.raw.sinesweepwav);
 
         testExtractor(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
-        testExtractor(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz);
+        testExtractor(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
         testExtractor(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz);
         testExtractor(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
         testExtractor(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
@@ -193,7 +193,7 @@
             testDecoder(R.raw.sinesweepwav) +
 
             testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
-            testDecoder(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz) +
+            testDecoder(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz) +
             testDecoder(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz) +
             testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) +
             testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
@@ -391,7 +391,7 @@
             testVideoPlayback(
                     R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
             testVideoPlayback(
-                    R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz) +
+                    R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz) +
             testVideoPlayback(
                     R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz) +
             testVideoPlayback(
diff --git a/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
index 3ba1ce8..86bab41 100644
--- a/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
+++ b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
@@ -18,8 +18,11 @@
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioTrack;
+import android.media.AudioAttributes;
 import android.util.Log;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.LinkedList;
 
 /**
@@ -31,22 +34,18 @@
 public class NonBlockingAudioTrack {
     private static final String TAG = NonBlockingAudioTrack.class.getSimpleName();
 
-    class QueueElem {
-        byte[] data;
-        int offset;
+    class QueueElement {
+        ByteBuffer data;
         int size;
     }
 
     private AudioTrack mAudioTrack;
-    private boolean mWriteMorePending = false;
     private int mSampleRate;
-    private int mFrameSize;
-    private int mBufferSizeInFrames;
-    private int mNumFramesSubmitted = 0;
     private int mNumBytesQueued = 0;
-    private LinkedList<QueueElem> mQueue = new LinkedList<QueueElem>();
+    private LinkedList<QueueElement> mQueue = new LinkedList<QueueElement>();
 
-    public NonBlockingAudioTrack(int sampleRate, int channelCount) {
+    public NonBlockingAudioTrack(int sampleRate, int channelCount, boolean hwAvSync,
+                    int audioSessionId) {
         int channelConfig;
         switch (channelCount) {
             case 1:
@@ -70,17 +69,31 @@
 
         int bufferSize = 2 * minBufferSize;
 
-        mAudioTrack = new AudioTrack(
-                AudioManager.STREAM_MUSIC,
-                sampleRate,
-                channelConfig,
-                AudioFormat.ENCODING_PCM_16BIT,
-                bufferSize,
-                AudioTrack.MODE_STREAM);
+        if (!hwAvSync) {
+            mAudioTrack = new AudioTrack(
+                    AudioManager.STREAM_MUSIC,
+                    sampleRate,
+                    channelConfig,
+                    AudioFormat.ENCODING_PCM_16BIT,
+                    bufferSize,
+                    AudioTrack.MODE_STREAM);
+        }
+        else {
+            // build AudioTrack using Audio Attributes and FLAG_HW_AV_SYNC
+            AudioAttributes audioAttributes = (new AudioAttributes.Builder())
+                            .setLegacyStreamType(AudioManager.STREAM_MUSIC)
+                            .setFlags(AudioAttributes.FLAG_HW_AV_SYNC)
+                            .build();
+            AudioFormat audioFormat = (new AudioFormat.Builder())
+                            .setChannelMask(channelConfig)
+                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                            .setSampleRate(sampleRate)
+                            .build();
+             mAudioTrack = new AudioTrack(audioAttributes, audioFormat, bufferSize,
+                                    AudioTrack.MODE_STREAM, audioSessionId);
+        }
 
         mSampleRate = sampleRate;
-        mFrameSize = 2 * channelCount;
-        mBufferSizeInFrames = bufferSize / mFrameSize;
     }
 
     public long getAudioTimeUs() {
@@ -98,105 +111,82 @@
     }
 
     public void stop() {
-        cancelWriteMore();
-
         mAudioTrack.stop();
 
-        mNumFramesSubmitted = 0;
         mQueue.clear();
         mNumBytesQueued = 0;
     }
 
     public void pause() {
-        cancelWriteMore();
-
         mAudioTrack.pause();
     }
 
-    public void release() {
-        cancelWriteMore();
+    public void flush() {
+        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
+            return;
+        }
+        mAudioTrack.flush();
+        mQueue.clear();
+        mNumBytesQueued = 0;
+    }
 
+    public void release() {
+        mQueue.clear();
+        mNumBytesQueued = 0;
         mAudioTrack.release();
         mAudioTrack = null;
     }
 
     public void process() {
-        mWriteMorePending = false;
-        writeMore();
+        while (!mQueue.isEmpty()) {
+            QueueElement element = mQueue.peekFirst();
+            int written = mAudioTrack.write(element.data, element.size,
+                                            AudioTrack.WRITE_NON_BLOCKING);
+            if (written < 0) {
+                throw new RuntimeException("Audiotrack.write() failed.");
+            }
+
+            mNumBytesQueued -= written;
+            element.size -= written;
+            if (element.size != 0) {
+                break;
+            }
+            mQueue.removeFirst();
+        }
     }
 
     public int getPlayState() {
         return mAudioTrack.getPlayState();
     }
 
-    private void writeMore() {
-        if (mQueue.isEmpty()) {
-            return;
-        }
+    public void write(ByteBuffer data, int size, long pts) {
+        // create timestamp header
+        final int headerSize = 16;
+        ByteBuffer avSyncHeader;
+        avSyncHeader = ByteBuffer.allocate(headerSize);
+        avSyncHeader.order(ByteOrder.BIG_ENDIAN);
+        avSyncHeader.putInt(0x55550001);
+        avSyncHeader.putInt(size);
+        avSyncHeader.putLong(pts);
+        avSyncHeader.position(0);
 
-        int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
-        int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
-        int numFramesAvailableToWrite = mBufferSizeInFrames - numFramesPending;
-        int numBytesAvailableToWrite = numFramesAvailableToWrite * mFrameSize;
+        QueueElement headerElement = new QueueElement();
+        headerElement.data = avSyncHeader;
+        headerElement.size = headerSize;
 
-        while (numBytesAvailableToWrite > 0) {
-            QueueElem elem = mQueue.peekFirst();
+        // accumulate size written to queue
+        mNumBytesQueued += headerSize;
+        mQueue.add(headerElement);
 
-            int numBytes = elem.size;
-            if (numBytes > numBytesAvailableToWrite) {
-                numBytes = numBytesAvailableToWrite;
-            }
-
-            int written = mAudioTrack.write(elem.data, elem.offset, numBytes);
-            assert(written == numBytes);
-
-            mNumFramesSubmitted += written / mFrameSize;
-
-            elem.size -= numBytes;
-            numBytesAvailableToWrite -= numBytes;
-            mNumBytesQueued -= numBytes;
-
-            if (elem.size == 0) {
-                mQueue.removeFirst();
-
-                if (mQueue.isEmpty()) {
-                    break;
-                }
-            } else {
-                elem.offset += numBytes;
-            }
-        }
-
-        if (!mQueue.isEmpty()) {
-            scheduleWriteMore();
-        }
-    }
-
-    private void scheduleWriteMore() {
-        if (mWriteMorePending) {
-            return;
-        }
-
-        int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
-        int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
-        int pendingDurationMs = 1000 * numFramesPending / mSampleRate;
-
-        mWriteMorePending = true;
-    }
-
-    private void cancelWriteMore() {
-        mWriteMorePending = false;
-    }
-
-    public void write(byte[] data, int size) {
-        QueueElem elem = new QueueElem();
-        elem.data = data;
-        elem.offset = 0;
-        elem.size = size;
+        // create payload element
+        QueueElement element = new QueueElement();
+        element.data = data;
+        element.size = size;
+        data.position(0);
 
         // accumulate size written to queue
         mNumBytesQueued += size;
-        mQueue.add(elem);
+        mQueue.add(element);
     }
 }
 
diff --git a/tests/tests/media/src/android/media/cts/OutputSurface.java b/tests/tests/media/src/android/media/cts/OutputSurface.java
index fc8ad9c..c87326d 100644
--- a/tests/tests/media/src/android/media/cts/OutputSurface.java
+++ b/tests/tests/media/src/android/media/cts/OutputSurface.java
@@ -70,7 +70,7 @@
         eglSetup(width, height);
         makeCurrent();
 
-        setup();
+        setup(this);
     }
 
     /**
@@ -78,14 +78,18 @@
      * new one).  Creates a Surface that can be passed to MediaCodec.configure().
      */
     public OutputSurface() {
-        setup();
+        setup(this);
+    }
+
+    public OutputSurface(final SurfaceTexture.OnFrameAvailableListener listener) {
+        setup(listener);
     }
 
     /**
      * Creates instances of TextureRender and SurfaceTexture, and a Surface associated
      * with the SurfaceTexture.
      */
-    private void setup() {
+    private void setup(SurfaceTexture.OnFrameAvailableListener listener) {
         mTextureRender = new TextureRender();
         mTextureRender.surfaceCreated();
 
@@ -107,7 +111,7 @@
         //
         // Java language note: passing "this" out of a constructor is generally unwise,
         // but we should be able to get away with it here.
-        mSurfaceTexture.setOnFrameAvailableListener(this);
+        mSurfaceTexture.setOnFrameAvailableListener(listener);
 
         mSurface = new Surface(mSurfaceTexture);
     }
@@ -285,6 +289,11 @@
         mTextureRender.drawFrame(mSurfaceTexture);
     }
 
+    public void latchImage() {
+        mTextureRender.checkGlError("before updateTexImage");
+        mSurfaceTexture.updateTexImage();
+    }
+
     @Override
     public void onFrameAvailable(SurfaceTexture st) {
         if (VERBOSE) Log.d(TAG, "new frame available");
diff --git a/tests/tests/media/src/android/media/cts/PostProcTestBase.java b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
index b034763..ef87662 100644
--- a/tests/tests/media/src/android/media/cts/PostProcTestBase.java
+++ b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
@@ -16,7 +16,9 @@
 
 package android.media.cts;
 
+import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.AudioManager;
 import android.media.audiofx.AudioEffect;
 import android.os.Looper;
 import android.test.AndroidTestCase;
@@ -29,6 +31,8 @@
     protected Looper mLooper = null;
     protected final Object mLock = new Object();
     protected int mChangedParameter = -1;
+    protected final static String BUNDLE_VOLUME_EFFECT_UUID =
+            "119341a0-8469-11df-81f9-0002a5d5c51b";
 
     protected boolean hasAudioOutput() {
         return getContext().getPackageManager().hasSystemFeature(
@@ -62,4 +66,13 @@
         }
         return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_ENV_REVERB);
     }
+
+    protected int getSessionId() {
+        AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+        assertNotNull("could not get AudioManager", am);
+        int sessionId = am.generateAudioSessionId();
+        assertTrue("Could not generate session id", sessionId>0);
+        return sessionId;
+    }
+
 }
diff --git a/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
new file mode 100644
index 0000000..35e88f0
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.media.MediaDescription;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Assert;
+
+/**
+ * Stub implementation of (@link android.service.media.MediaBrowserService}.
+ */
+public class StubMediaBrowserService extends MediaBrowserService {
+    static final String MEDIA_ID_ROOT = "test_media_id_root";
+    static final String EXTRAS_KEY = "test_extras_key";
+    static final String EXTRAS_VALUE = "test_extras_value";
+    static final String MEDIA_ID_CHILDREN_DELAYED = "test_media_id_children_delayed";
+    static final String[] MEDIA_ID_CHILDREN = new String[] {
+        "test_media_id_children_0", "test_media_id_children_1",
+        "test_media_id_children_2", "test_media_id_children_3",
+        MEDIA_ID_CHILDREN_DELAYED
+    };
+
+    static StubMediaBrowserService sInstance;
+
+    /* package private */ static MediaSession sSession;
+    private Bundle mExtras;
+    private Result<List<MediaItem>> mPendingResult;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sInstance = this;
+        sSession = new MediaSession(this, "StubMediaBrowserService");
+        setSessionToken(sSession.getSessionToken());
+    }
+
+    @Override
+    public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+        mExtras = new Bundle();
+        mExtras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+        return new BrowserRoot(MEDIA_ID_ROOT, mExtras);
+    }
+
+    @Override
+    public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+        List<MediaItem> mediaItems = new ArrayList<>();
+        if (MEDIA_ID_ROOT.equals(parentMediaId)) {
+            for (String id : MEDIA_ID_CHILDREN) {
+                mediaItems.add(new MediaItem(new MediaDescription.Builder()
+                        .setMediaId(id).build(), MediaItem.FLAG_BROWSABLE));
+            }
+            result.sendResult(mediaItems);
+        } else if (MEDIA_ID_CHILDREN_DELAYED.equals(parentMediaId)) {
+            Assert.assertNull(mPendingResult);
+            mPendingResult = result;
+            result.detach();
+        }
+    }
+
+    public void sendDelayedNotifyChildrenChanged() {
+        if (mPendingResult != null) {
+            mPendingResult.sendResult(Collections.<MediaItem>emptyList());
+            mPendingResult = null;
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
new file mode 100644
index 0000000..858e47c
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -0,0 +1,1606 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import com.android.cts.media.R;
+
+import android.media.cts.CodecUtils;
+
+import android.cts.util.MediaUtils;
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.Image;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.net.Uri;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+public class VideoEncoderTest extends MediaPlayerTestBase {
+    private static final int MAX_SAMPLE_SIZE = 256 * 1024;
+    private static final String TAG = "VideoEncoderTest";
+    private static final long FRAME_TIMEOUT_MS = 1000;
+
+    private static final String SOURCE_URL =
+        "android.resource://com.android.cts.media/raw/video_480x360_mp4_h264_871kbps_30fps";
+
+    private final boolean DEBUG = false;
+
+    class VideoStorage {
+        private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
+        private MediaFormat mFormat;
+        private int mInputBufferSize;
+
+        public VideoStorage() {
+            mStream = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
+        }
+
+        public void setFormat(MediaFormat format) {
+            mFormat = format;
+        }
+
+        public void addBuffer(ByteBuffer buffer, BufferInfo info) {
+            ByteBuffer savedBuffer = ByteBuffer.allocate(info.size);
+            savedBuffer.put(buffer);
+            if (info.size > mInputBufferSize) {
+                mInputBufferSize = info.size;
+            }
+            BufferInfo savedInfo = new BufferInfo();
+            savedInfo.set(0, savedBuffer.position(), info.presentationTimeUs, info.flags);
+            mStream.addLast(Pair.create(savedBuffer, savedInfo));
+        }
+
+        private void play(MediaCodec decoder, Surface surface) {
+            decoder.reset();
+            final Object condition = new Object();
+            final Iterator<Pair<ByteBuffer, BufferInfo>> it = mStream.iterator();
+            decoder.setCallback(new MediaCodec.Callback() {
+                public void onOutputBufferAvailable(MediaCodec codec, int ix, BufferInfo info) {
+                    codec.releaseOutputBuffer(ix, info.size > 0);
+                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        synchronized (condition) {
+                            condition.notifyAll();
+                        }
+                    }
+                }
+                public void onInputBufferAvailable(MediaCodec codec, int ix) {
+                    if (it.hasNext()) {
+                        Pair<ByteBuffer, BufferInfo> el = it.next();
+                        el.first.clear();
+                        try {
+                            codec.getInputBuffer(ix).put(el.first);
+                        } catch (java.nio.BufferOverflowException e) {
+                            Log.e(TAG, "cannot fit " + el.first.limit()
+                                    + "-byte encoded buffer into "
+                                    + codec.getInputBuffer(ix).remaining()
+                                    + "-byte input buffer of " + codec.getName()
+                                    + " configured for " + codec.getInputFormat());
+                            throw e;
+                        }
+                        BufferInfo info = el.second;
+                        codec.queueInputBuffer(
+                                ix, 0, info.size, info.presentationTimeUs, info.flags);
+                    }
+                }
+                public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+                    Log.i(TAG, "got codec exception", e);
+                    fail("received codec error during decode" + e);
+                }
+                public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+                    Log.i(TAG, "got output format " + format);
+                }
+            });
+            mFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mInputBufferSize);
+            decoder.configure(mFormat, surface, null /* crypto */, 0 /* flags */);
+            decoder.start();
+            synchronized (condition) {
+                try {
+                    condition.wait();
+                } catch (InterruptedException e) {
+                    fail("playback interrupted");
+                }
+            }
+            decoder.stop();
+        }
+
+        public void playAll(Surface surface) {
+            if (mFormat == null) {
+                Log.i(TAG, "no stream to play");
+                return;
+            }
+            String mime = mFormat.getString(MediaFormat.KEY_MIME);
+            MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+            for (MediaCodecInfo info : mcl.getCodecInfos()) {
+                if (info.isEncoder()) {
+                    continue;
+                }
+                MediaCodec codec = null;
+                try {
+                    CodecCapabilities caps = info.getCapabilitiesForType(mime);
+                    if (!caps.isFormatSupported(mFormat)) {
+                        continue;
+                    }
+                    codec = MediaCodec.createByCodecName(info.getName());
+                } catch (IllegalArgumentException | IOException e) {
+                    continue;
+                }
+                play(codec, surface);
+                codec.release();
+            }
+        }
+    }
+
+    abstract class VideoProcessorBase extends MediaCodec.Callback {
+        private static final String TAG = "VideoProcessorBase";
+
+        private MediaExtractor mExtractor;
+        private ByteBuffer mBuffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE);
+        private int mTrackIndex = -1;
+        private boolean mSignaledDecoderEOS;
+
+        protected boolean mCompleted;
+        protected final Object mCondition = new Object();
+
+        protected MediaFormat mDecFormat;
+        protected MediaCodec mDecoder, mEncoder;
+
+        private VideoStorage mEncodedStream;
+        protected int mFrameRate = 0;
+        protected int mBitRate = 0;
+
+        protected void open(String path) throws IOException {
+            mExtractor = new MediaExtractor();
+            if (path.startsWith("android.resource://")) {
+                mExtractor.setDataSource(mContext, Uri.parse(path), null);
+            } else {
+                mExtractor.setDataSource(path);
+            }
+
+            for (int i = 0; i < mExtractor.getTrackCount(); i++) {
+                MediaFormat fmt = mExtractor.getTrackFormat(i);
+                String mime = fmt.getString(MediaFormat.KEY_MIME).toLowerCase();
+                if (mime.startsWith("video/")) {
+                    mTrackIndex = i;
+                    mDecFormat = fmt;
+                    mExtractor.selectTrack(i);
+                    break;
+                }
+            }
+            mEncodedStream = new VideoStorage();
+            assertTrue("file " + path + " has no video", mTrackIndex >= 0);
+        }
+
+        // returns true if encoder supports the size
+        protected boolean initCodecsAndConfigureEncoder(
+                String videoEncName, String outMime, int width, int height, int colorFormat)
+                        throws IOException {
+            mDecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+
+            MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+            String videoDecName = mcl.findDecoderForFormat(mDecFormat);
+            Log.i(TAG, "decoder for " + mDecFormat + " is " + videoDecName);
+            mDecoder = MediaCodec.createByCodecName(videoDecName);
+            mEncoder = MediaCodec.createByCodecName(videoEncName);
+
+            mDecoder.setCallback(this);
+            mEncoder.setCallback(this);
+
+            VideoCapabilities encCaps =
+                mEncoder.getCodecInfo().getCapabilitiesForType(outMime).getVideoCapabilities();
+            if (!encCaps.isSizeSupported(width, height)) {
+                Log.i(TAG, videoEncName + " does not support size: " + width + "x" + height);
+                return false;
+            }
+
+            MediaFormat outFmt = MediaFormat.createVideoFormat(outMime, width, height);
+
+            {
+                int maxWidth = encCaps.getSupportedWidths().getUpper();
+                int maxHeight = encCaps.getSupportedHeightsFor(maxWidth).getUpper();
+                int frameRate = mFrameRate;
+                if (frameRate <= 0) {
+                    int maxRate =
+                        encCaps.getSupportedFrameRatesFor(maxWidth, maxHeight)
+                        .getUpper().intValue();
+                    frameRate = Math.min(30, maxRate);
+                }
+                outFmt.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
+
+                int bitRate = mBitRate;
+                if (bitRate <= 0) {
+                    bitRate = encCaps.getBitrateRange().clamp(
+                        (int)(encCaps.getBitrateRange().getUpper() /
+                                Math.sqrt((double)maxWidth * maxHeight / width / height)));
+                }
+                outFmt.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+
+                Log.d(TAG, "frame rate = " + frameRate + ", bit rate = " + bitRate);
+            }
+            outFmt.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
+            outFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+            mEncoder.configure(outFmt, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            Log.i(TAG, "encoder input format " + mEncoder.getInputFormat() + " from " + outFmt);
+            return true;
+        }
+
+        protected void close() {
+            if (mDecoder != null) {
+                mDecoder.release();
+                mDecoder = null;
+            }
+            if (mEncoder != null) {
+                mEncoder.release();
+                mEncoder = null;
+            }
+            if (mExtractor != null) {
+                mExtractor.release();
+                mExtractor = null;
+            }
+        }
+
+        // returns true if filled buffer
+        protected boolean fillDecoderInputBuffer(int ix) {
+            if (DEBUG) Log.v(TAG, "decoder received input #" + ix);
+            while (!mSignaledDecoderEOS) {
+                int track = mExtractor.getSampleTrackIndex();
+                if (track >= 0 && track != mTrackIndex) {
+                    mExtractor.advance();
+                    continue;
+                }
+                int size = mExtractor.readSampleData(mBuffer, 0);
+                if (size < 0) {
+                    // queue decoder input EOS
+                    if (DEBUG) Log.v(TAG, "queuing decoder EOS");
+                    mDecoder.queueInputBuffer(
+                            ix, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                    mSignaledDecoderEOS = true;
+                } else {
+                    mBuffer.limit(size);
+                    mBuffer.position(0);
+                    BufferInfo info = new BufferInfo();
+                    info.set(
+                            0, mBuffer.limit(), mExtractor.getSampleTime(),
+                            mExtractor.getSampleFlags());
+                    mDecoder.getInputBuffer(ix).put(mBuffer);
+                    if (DEBUG) Log.v(TAG, "queing input #" + ix + " for decoder with timestamp "
+                            + info.presentationTimeUs);
+                    mDecoder.queueInputBuffer(
+                            ix, 0, mBuffer.limit(), info.presentationTimeUs, 0);
+                }
+                mExtractor.advance();
+                return true;
+            }
+            return false;
+        }
+
+        protected void emptyEncoderOutputBuffer(int ix, BufferInfo info) {
+            if (DEBUG) Log.v(TAG, "encoder received output #" + ix
+                     + " (sz=" + info.size + ", f=" + info.flags
+                     + ", ts=" + info.presentationTimeUs + ")");
+            mEncodedStream.addBuffer(mEncoder.getOutputBuffer(ix), info);
+            if (!mCompleted) {
+                mEncoder.releaseOutputBuffer(ix, false);
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "encoder received output EOS");
+                    synchronized(mCondition) {
+                        mCompleted = true;
+                        mCondition.notifyAll(); // condition is always satisfied
+                    }
+                }
+            }
+        }
+
+        protected void saveEncoderFormat(MediaFormat format) {
+            mEncodedStream.setFormat(format);
+        }
+
+        public void playBack(Surface surface) {
+            mEncodedStream.playAll(surface);
+        }
+
+        public void setFrameAndBitRates(int frameRate, int bitRate) {
+            mFrameRate = frameRate;
+            mBitRate = bitRate;
+        }
+
+        public abstract boolean processLoop(
+                String path, String outMime, String videoEncName,
+                int width, int height, boolean optional);
+    };
+
+    class VideoProcessor extends VideoProcessorBase {
+        private static final String TAG = "VideoProcessor";
+        private boolean mWorkInProgress;
+        private boolean mGotDecoderEOS;
+        private boolean mSignaledEncoderEOS;
+
+        private LinkedList<Pair<Integer, BufferInfo>> mBuffersToRender =
+            new LinkedList<Pair<Integer, BufferInfo>>();
+        private LinkedList<Integer> mEncInputBuffers = new LinkedList<Integer>();
+
+        private int mEncInputBufferSize = -1;
+
+        @Override
+        public boolean processLoop(
+                 String path, String outMime, String videoEncName,
+                 int width, int height, boolean optional) {
+            boolean skipped = true;
+            try {
+                open(path);
+                if (!initCodecsAndConfigureEncoder(
+                        videoEncName, outMime, width, height,
+                        CodecCapabilities.COLOR_FormatYUV420Flexible)) {
+                    assertTrue("could not configure encoder for supported size", optional);
+                    return !skipped;
+                }
+                skipped = false;
+
+                mDecoder.configure(mDecFormat, null /* surface */, null /* crypto */, 0);
+
+                mDecoder.start();
+                mEncoder.start();
+
+                // main loop - process GL ops as only main thread has GL context
+                while (!mCompleted) {
+                    Pair<Integer, BufferInfo> decBuffer = null;
+                    int encBuffer = -1;
+                    synchronized (mCondition) {
+                        try {
+                            // wait for an encoder input buffer and a decoder output buffer
+                            // Use a timeout to avoid stalling the test if it doesn't arrive.
+                            if (!haveBuffers() && !mCompleted) {
+                                mCondition.wait(FRAME_TIMEOUT_MS);
+                            }
+                        } catch (InterruptedException ie) {
+                            fail("wait interrupted");  // shouldn't happen
+                        }
+                        if (mCompleted) {
+                            break;
+                        }
+                        if (!haveBuffers()) {
+                            fail("timed out after " + mBuffersToRender.size()
+                                    + " decoder output and " + mEncInputBuffers.size()
+                                    + " encoder input buffers");
+                        }
+
+                        if (DEBUG) Log.v(TAG, "got image");
+                        decBuffer = mBuffersToRender.removeFirst();
+                        encBuffer = mEncInputBuffers.removeFirst();
+                        if (isEOSOnlyBuffer(decBuffer)) {
+                            queueEncoderEOS(decBuffer, encBuffer);
+                            continue;
+                        }
+                        mWorkInProgress = true;
+                    }
+
+                    if (mWorkInProgress) {
+                        renderDecodedBuffer(decBuffer, encBuffer);
+                        synchronized(mCondition) {
+                            mWorkInProgress = false;
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                fail("received exception " + e);
+            } finally {
+                close();
+            }
+            return !skipped;
+        }
+
+        @Override
+        public void onInputBufferAvailable(MediaCodec mediaCodec, int ix) {
+            if (mediaCodec == mDecoder) {
+                // fill input buffer from extractor
+                fillDecoderInputBuffer(ix);
+            } else if (mediaCodec == mEncoder) {
+                synchronized(mCondition) {
+                    mEncInputBuffers.addLast(ix);
+                    tryToPropagateEOS();
+                    if (haveBuffers()) {
+                        mCondition.notifyAll();
+                    }
+                }
+            } else {
+                fail("received input buffer on " + mediaCodec.getName());
+            }
+        }
+
+        @Override
+        public void onOutputBufferAvailable(
+                MediaCodec mediaCodec, int ix, BufferInfo info) {
+            if (mediaCodec == mDecoder) {
+                if (DEBUG) Log.v(TAG, "decoder received output #" + ix
+                         + " (sz=" + info.size + ", f=" + info.flags
+                         + ", ts=" + info.presentationTimeUs + ")");
+                // render output buffer from decoder
+                if (!mGotDecoderEOS) {
+                    boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+                    // can release empty buffers now
+                    if (info.size == 0) {
+                        mDecoder.releaseOutputBuffer(ix, false /* render */);
+                        ix = -1; // dummy index used by render to not render
+                    }
+                    synchronized(mCondition) {
+                        if (ix < 0 && eos && mBuffersToRender.size() > 0) {
+                            // move lone EOS flag to last buffer to be rendered
+                            mBuffersToRender.peekLast().second.flags |=
+                                MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                        } else if (ix >= 0 || eos) {
+                            mBuffersToRender.addLast(Pair.create(ix, info));
+                        }
+                        if (eos) {
+                            tryToPropagateEOS();
+                            mGotDecoderEOS = true;
+                        }
+                        if (haveBuffers()) {
+                            mCondition.notifyAll();
+                        }
+                    }
+                }
+            } else if (mediaCodec == mEncoder) {
+                emptyEncoderOutputBuffer(ix, info);
+            } else {
+                fail("received output buffer on " + mediaCodec.getName());
+            }
+        }
+
+        private void renderDecodedBuffer(Pair<Integer, BufferInfo> decBuffer, int encBuffer) {
+            // process heavyweight actions under instance lock
+            Image encImage = mEncoder.getInputImage(encBuffer);
+            Image decImage = mDecoder.getOutputImage(decBuffer.first);
+            assertNotNull("could not get encoder image for " + mEncoder.getInputFormat(), encImage);
+            assertNotNull("could not get decoder image for " + mDecoder.getInputFormat(), decImage);
+            assertEquals("incorrect decoder format",decImage.getFormat(), ImageFormat.YUV_420_888);
+            assertEquals("incorrect encoder format", encImage.getFormat(), ImageFormat.YUV_420_888);
+
+            CodecUtils.copyFlexYUVImage(encImage, decImage);
+
+            // TRICKY: need this for queueBuffer
+            if (mEncInputBufferSize < 0) {
+                mEncInputBufferSize = mEncoder.getInputBuffer(encBuffer).capacity();
+            }
+            Log.d(TAG, "queuing output #" + encBuffer + " for encoder (sz="
+                    + mEncInputBufferSize + ", f=" + decBuffer.second.flags
+                    + ", ts=" + decBuffer.second.presentationTimeUs + ")");
+            mEncoder.queueInputBuffer(
+                    encBuffer, 0, mEncInputBufferSize, decBuffer.second.presentationTimeUs,
+                    decBuffer.second.flags);
+            if ((decBuffer.second.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                mSignaledEncoderEOS = true;
+            }
+            mDecoder.releaseOutputBuffer(decBuffer.first, false /* render */);
+        }
+
+        @Override
+        public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
+            fail("received error on " + mediaCodec.getName() + ": " + e);
+        }
+
+        @Override
+        public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
+            Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
+            if (mediaCodec == mEncoder) {
+                saveEncoderFormat(mediaFormat);
+            }
+        }
+
+        // next methods are synchronized on mCondition
+        private boolean haveBuffers() {
+            return mEncInputBuffers.size() > 0 && mBuffersToRender.size() > 0
+                    && !mSignaledEncoderEOS;
+        }
+
+        private boolean isEOSOnlyBuffer(Pair<Integer, BufferInfo> decBuffer) {
+            return decBuffer.first < 0 || decBuffer.second.size == 0;
+        }
+
+        protected void tryToPropagateEOS() {
+            if (!mWorkInProgress && haveBuffers() && isEOSOnlyBuffer(mBuffersToRender.getFirst())) {
+                Pair<Integer, BufferInfo> decBuffer = mBuffersToRender.removeFirst();
+                int encBuffer = mEncInputBuffers.removeFirst();
+                queueEncoderEOS(decBuffer, encBuffer);
+            }
+        }
+
+        void queueEncoderEOS(Pair<Integer, BufferInfo> decBuffer, int encBuffer) {
+            Log.d(TAG, "signaling encoder EOS");
+            mEncoder.queueInputBuffer(encBuffer, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+            mSignaledEncoderEOS = true;
+            if (decBuffer.first >= 0) {
+                mDecoder.releaseOutputBuffer(decBuffer.first, false /* render */);
+            }
+        }
+    }
+
+
+    class SurfaceVideoProcessor extends VideoProcessorBase
+            implements SurfaceTexture.OnFrameAvailableListener {
+        private static final String TAG = "SurfaceVideoProcessor";
+        private boolean mFrameAvailable;
+        private boolean mEncoderIsActive;
+        private boolean mGotDecoderEOS;
+        private boolean mSignaledEncoderEOS;
+
+        private InputSurface mEncSurface;
+        private OutputSurface mDecSurface;
+        private BufferInfo mInfoOnSurface;
+
+        private LinkedList<Pair<Integer, BufferInfo>> mBuffersToRender =
+            new LinkedList<Pair<Integer, BufferInfo>>();
+
+        @Override
+        public boolean processLoop(
+                String path, String outMime, String videoEncName,
+                int width, int height, boolean optional) {
+            boolean skipped = true;
+            try {
+                open(path);
+                if (!initCodecsAndConfigureEncoder(
+                        videoEncName, outMime, width, height,
+                        CodecCapabilities.COLOR_FormatSurface)) {
+                    assertTrue("could not configure encoder for supported size", optional);
+                    return !skipped;
+                }
+                skipped = false;
+
+                mEncSurface = new InputSurface(mEncoder.createInputSurface());
+                mEncSurface.makeCurrent();
+
+                mDecSurface = new OutputSurface(this);
+                //mDecSurface.changeFragmentShader(FRAGMENT_SHADER);
+                mDecoder.configure(mDecFormat, mDecSurface.getSurface(), null /* crypto */, 0);
+
+                mDecoder.start();
+                mEncoder.start();
+
+                // main loop - process GL ops as only main thread has GL context
+                while (!mCompleted) {
+                    BufferInfo info = null;
+                    synchronized (mCondition) {
+                        try {
+                            // wait for mFrameAvailable, which is set by onFrameAvailable().
+                            // Use a timeout to avoid stalling the test if it doesn't arrive.
+                            if (!mFrameAvailable && !mCompleted && !mEncoderIsActive) {
+                                mCondition.wait(FRAME_TIMEOUT_MS);
+                            }
+                        } catch (InterruptedException ie) {
+                            fail("wait interrupted");  // shouldn't happen
+                        }
+                        if (mCompleted) {
+                            break;
+                        }
+                        if (mEncoderIsActive) {
+                            mEncoderIsActive = false;
+                            if (DEBUG) Log.d(TAG, "encoder is still active, continue");
+                            continue;
+                        }
+                        assertTrue("still waiting for image", mFrameAvailable);
+                        if (DEBUG) Log.v(TAG, "got image");
+                        info = mInfoOnSurface;
+                    }
+                    if (info == null) {
+                        continue;
+                    }
+                    if (info.size > 0) {
+                        mDecSurface.latchImage();
+                        if (DEBUG) Log.v(TAG, "latched image");
+                        mFrameAvailable = false;
+
+                        mDecSurface.drawImage();
+                        Log.d(TAG, "encoding frame at " + info.presentationTimeUs * 1000);
+
+                        mEncSurface.setPresentationTime(info.presentationTimeUs * 1000);
+                        mEncSurface.swapBuffers();
+                    }
+                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        mSignaledEncoderEOS = true;
+                        Log.d(TAG, "signaling encoder EOS");
+                        mEncoder.signalEndOfInputStream();
+                    }
+
+                    synchronized (mCondition) {
+                        mInfoOnSurface = null;
+                        if (mBuffersToRender.size() > 0 && mInfoOnSurface == null) {
+                            if (DEBUG) Log.v(TAG, "handling postponed frame");
+                            Pair<Integer, BufferInfo> nextBuffer = mBuffersToRender.removeFirst();
+                            renderDecodedBuffer(nextBuffer.first, nextBuffer.second);
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                fail("received exception " + e);
+            } finally {
+                close();
+                if (mEncSurface != null) {
+                    mEncSurface.release();
+                    mEncSurface = null;
+                }
+                if (mDecSurface != null) {
+                    mDecSurface.release();
+                    mDecSurface = null;
+                }
+            }
+            return !skipped;
+        }
+
+        @Override
+        public void onFrameAvailable(SurfaceTexture st) {
+            if (DEBUG) Log.v(TAG, "new frame available");
+            synchronized (mCondition) {
+                assertFalse("mFrameAvailable already set, frame could be dropped", mFrameAvailable);
+                mFrameAvailable = true;
+                mCondition.notifyAll();
+            }
+        }
+
+        @Override
+        public void onInputBufferAvailable(MediaCodec mediaCodec, int ix) {
+            if (mediaCodec == mDecoder) {
+                // fill input buffer from extractor
+                fillDecoderInputBuffer(ix);
+            } else {
+                fail("received input buffer on " + mediaCodec.getName());
+            }
+        }
+
+        @Override
+        public void onOutputBufferAvailable(
+                MediaCodec mediaCodec, int ix, BufferInfo info) {
+            if (mediaCodec == mDecoder) {
+                if (DEBUG) Log.v(TAG, "decoder received output #" + ix
+                         + " (sz=" + info.size + ", f=" + info.flags
+                         + ", ts=" + info.presentationTimeUs + ")");
+                // render output buffer from decoder
+                if (!mGotDecoderEOS) {
+                    boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+                    if (eos) {
+                        mGotDecoderEOS = true;
+                    }
+                    // can release empty buffers now
+                    if (info.size == 0) {
+                        mDecoder.releaseOutputBuffer(ix, false /* render */);
+                        ix = -1; // dummy index used by render to not render
+                    }
+                    if (eos || info.size > 0) {
+                        synchronized(mCondition) {
+                            if (mInfoOnSurface != null || mBuffersToRender.size() > 0) {
+                                if (DEBUG) Log.v(TAG, "postponing render, surface busy");
+                                mBuffersToRender.addLast(Pair.create(ix, info));
+                            } else {
+                                renderDecodedBuffer(ix, info);
+                            }
+                        }
+                    }
+                }
+            } else if (mediaCodec == mEncoder) {
+                emptyEncoderOutputBuffer(ix, info);
+                synchronized(mCondition) {
+                    if (!mCompleted) {
+                        mEncoderIsActive = true;
+                        mCondition.notifyAll();
+                    }
+                }
+            } else {
+                fail("received output buffer on " + mediaCodec.getName());
+            }
+        }
+
+        private void renderDecodedBuffer(int ix, BufferInfo info) {
+            boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+            mInfoOnSurface = info;
+            if (info.size > 0) {
+                Log.d(TAG, "rendering frame #" + ix + " at " + info.presentationTimeUs * 1000
+                        + (eos ? " with EOS" : ""));
+                mDecoder.releaseOutputBuffer(ix, info.presentationTimeUs * 1000);
+            }
+
+            if (eos && info.size == 0) {
+                if (DEBUG) Log.v(TAG, "decoder output EOS available");
+                mFrameAvailable = true;
+                mCondition.notifyAll();
+            }
+        }
+
+        @Override
+        public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
+            fail("received error on " + mediaCodec.getName() + ": " + e);
+        }
+
+        @Override
+        public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
+            Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
+            if (mediaCodec == mEncoder) {
+                saveEncoderFormat(mediaFormat);
+            }
+        }
+    }
+
+    class Encoder {
+        final private String mName;
+        final private String mMime;
+        final private VideoCapabilities mCaps;
+
+        final private Map<Size, Set<Size>> mMinMax;     // extreme sizes
+        final private Map<Size, Set<Size>> mNearMinMax; // sizes near extreme
+        final private Set<Size> mArbitraryW;            // arbitrary widths in the middle
+        final private Set<Size> mArbitraryH;            // arbitrary heights in the middle
+        final private Set<Size> mSizes;                 // all non-specifically tested sizes
+
+        final private int xAlign;
+        final private int yAlign;
+
+        Encoder(String name, String mime, CodecCapabilities caps) {
+            mName = name;
+            mMime = mime;
+            mCaps = caps.getVideoCapabilities();
+
+            /* calculate min/max sizes */
+            mMinMax = new HashMap<Size, Set<Size>>();
+            mNearMinMax = new HashMap<Size, Set<Size>>();
+            mArbitraryW = new HashSet<Size>();
+            mArbitraryH = new HashSet<Size>();
+            mSizes = new HashSet<Size>();
+
+            xAlign = mCaps.getWidthAlignment();
+            yAlign = mCaps.getHeightAlignment();
+
+            initializeSizes();
+        }
+
+        private void initializeSizes() {
+            for (int x = 0; x < 2; ++x) {
+                for (int y = 0; y < 2; ++y) {
+                    addExtremeSizesFor(x, y);
+                }
+            }
+
+            // initialize arbitrary sizes
+            for (int i = 1; i <= 7; ++i) {
+                int j = ((7 * i) % 11) + 1;
+                int width, height;
+                try {
+                    width = alignedPointInRange(i * 0.125, xAlign, mCaps.getSupportedWidths());
+                    height = alignedPointInRange(
+                            j * 0.077, yAlign, mCaps.getSupportedHeightsFor(width));
+                    mArbitraryW.add(new Size(width, height));
+                } catch (IllegalArgumentException e) {
+                }
+
+                try {
+                    height = alignedPointInRange(i * 0.125, yAlign, mCaps.getSupportedHeights());
+                    width = alignedPointInRange(j * 0.077, xAlign, mCaps.getSupportedWidthsFor(height));
+                    mArbitraryH.add(new Size(width, height));
+                } catch (IllegalArgumentException e) {
+                }
+            }
+            mArbitraryW.removeAll(mArbitraryH);
+            mArbitraryW.removeAll(mSizes);
+            mSizes.addAll(mArbitraryW);
+            mArbitraryH.removeAll(mSizes);
+            mSizes.addAll(mArbitraryH);
+            if (DEBUG) Log.i(TAG, "arbitrary=" + mArbitraryW + "/" + mArbitraryH);
+        }
+
+        private void addExtremeSizesFor(int x, int y) {
+            Set<Size> minMax = new HashSet<Size>();
+            Set<Size> nearMinMax = new HashSet<Size>();
+
+            for (int dx = 0; dx <= xAlign; dx += xAlign) {
+                for (int dy = 0; dy <= yAlign; dy += yAlign) {
+                    Set<Size> bucket = (dx + dy == 0) ? minMax : nearMinMax;
+                    try {
+                        int width = getExtreme(mCaps.getSupportedWidths(), x, dx);
+                        int height = getExtreme(mCaps.getSupportedHeightsFor(width), y, dy);
+                        bucket.add(new Size(width, height));
+
+                        // try max max with more reasonable ratio if too skewed
+                        if (x + y == 2 && width >= 4 * height) {
+                            Size wideScreen = getLargestSizeForRatio(16, 9);
+                            width = getExtreme(
+                                    mCaps.getSupportedWidths()
+                                            .intersect(0, wideScreen.getWidth()), x, dx);
+                            height = getExtreme(mCaps.getSupportedHeightsFor(width), y, 0);
+                            bucket.add(new Size(width, height));
+                        }
+                    } catch (IllegalArgumentException e) {
+                    }
+
+                    try {
+                        int height = getExtreme(mCaps.getSupportedHeights(), y, dy);
+                        int width = getExtreme(mCaps.getSupportedWidthsFor(height), x, dx);
+                        bucket.add(new Size(width, height));
+
+                        // try max max with more reasonable ratio if too skewed
+                        if (x + y == 2 && height >= 4 * width) {
+                            Size wideScreen = getLargestSizeForRatio(9, 16);
+                            height = getExtreme(
+                                    mCaps.getSupportedHeights()
+                                            .intersect(0, wideScreen.getHeight()), y, dy);
+                            width = getExtreme(mCaps.getSupportedWidthsFor(height), x, dx);
+                            bucket.add(new Size(width, height));
+                        }
+                    } catch (IllegalArgumentException e) {
+                    }
+                }
+            }
+
+            // keep unique sizes
+            minMax.removeAll(mSizes);
+            mSizes.addAll(minMax);
+            nearMinMax.removeAll(mSizes);
+            mSizes.addAll(nearMinMax);
+
+            mMinMax.put(new Size(x, y), minMax);
+            mNearMinMax.put(new Size(x, y), nearMinMax);
+            if (DEBUG) Log.i(TAG, x + "x" + y + ": minMax=" + mMinMax + ", near=" + mNearMinMax);
+        }
+
+        private int alignInRange(double value, int align, Range<Integer> range) {
+            return range.clamp(align * (int)Math.round(value / align));
+        }
+
+        /* point should be between 0. and 1. */
+        private int alignedPointInRange(double point, int align, Range<Integer> range) {
+            return alignInRange(
+                    range.getLower() + point * (range.getUpper() - range.getLower()), align, range);
+        }
+
+        private int getExtreme(Range<Integer> range, int i, int delta) {
+            int dim = i == 1 ? range.getUpper() - delta : range.getLower() + delta;
+            if (delta == 0
+                    || (dim > range.getLower() && dim < range.getUpper())) {
+                return dim;
+            }
+            throw new IllegalArgumentException();
+        }
+
+        private Size getLargestSizeForRatio(int x, int y) {
+            Range<Integer> widthRange = mCaps.getSupportedWidths();
+            Range<Integer> heightRange = mCaps.getSupportedHeightsFor(widthRange.getUpper());
+            final int xAlign = mCaps.getWidthAlignment();
+            final int yAlign = mCaps.getHeightAlignment();
+
+            // scale by alignment
+            int width = alignInRange(
+                    Math.sqrt(widthRange.getUpper() * heightRange.getUpper() * (double)x / y),
+                    xAlign, widthRange);
+            int height = alignInRange(
+                    width * (double)y / x, yAlign, mCaps.getSupportedHeightsFor(width));
+            return new Size(width, height);
+        }
+
+
+        public boolean testExtreme(int x, int y, boolean flexYUV, boolean near) {
+            boolean skipped = true;
+            for (Size s : (near ? mNearMinMax : mMinMax).get(new Size(x, y))) {
+                if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
+                    skipped = false;
+                }
+            }
+            return !skipped;
+        }
+
+        public boolean testArbitrary(boolean flexYUV, boolean widths) {
+            boolean skipped = true;
+            for (Size s : (widths ? mArbitraryW : mArbitraryH)) {
+                if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
+                    skipped = false;
+                }
+            }
+            return !skipped;
+        }
+
+        public boolean testSpecific(int width, int height, boolean flexYUV) {
+            // already tested by one of the min/max tests
+            if (mSizes.contains(new Size(width, height))) {
+                return false;
+            }
+            return test(width, height, true /* optional */, flexYUV);
+        }
+
+        public boolean testDetailed(
+                int width, int height, int frameRate, int bitRate, boolean flexYUV) {
+            return test(width, height, frameRate, bitRate, true /* optional */, flexYUV);
+        }
+
+        public boolean testSupport(int width, int height, int frameRate, int bitRate) {
+            return mCaps.areSizeAndRateSupported(width, height, frameRate) &&
+                    mCaps.getBitrateRange().contains(bitRate);
+        }
+
+        private boolean test(int width, int height, boolean optional, boolean flexYUV) {
+            return test(width, height, 0 /* frameRate */, 0 /* bitRate */, optional, flexYUV);
+        }
+
+        private boolean test(int width, int height, int frameRate, int bitRate,
+                boolean optional, boolean flexYUV) {
+            Log.i(TAG, "testing " + mMime + " on " + mName + " for " + width + "x" + height
+                    + (flexYUV ? " flexYUV" : " surface"));
+
+            VideoProcessorBase processor =
+                flexYUV ? new VideoProcessor() : new SurfaceVideoProcessor();
+
+            processor.setFrameAndBitRates(frameRate, bitRate);
+
+            // We are using a resource URL as an example
+            boolean success = processor.processLoop(
+                    SOURCE_URL, mMime, mName, width, height, optional);
+            if (success) {
+                processor.playBack(getActivity().getSurfaceHolder().getSurface());
+            }
+            return success;
+        }
+    }
+
+    private Encoder[] googH265()  { return goog(MediaFormat.MIMETYPE_VIDEO_HEVC); }
+    private Encoder[] googH264()  { return goog(MediaFormat.MIMETYPE_VIDEO_AVC); }
+    private Encoder[] googH263()  { return goog(MediaFormat.MIMETYPE_VIDEO_H263); }
+    private Encoder[] googMpeg4() { return goog(MediaFormat.MIMETYPE_VIDEO_MPEG4); }
+    private Encoder[] googVP8()   { return goog(MediaFormat.MIMETYPE_VIDEO_VP8); }
+    private Encoder[] googVP9()   { return goog(MediaFormat.MIMETYPE_VIDEO_VP9); }
+
+    private Encoder[] otherH265()  { return other(MediaFormat.MIMETYPE_VIDEO_HEVC); }
+    private Encoder[] otherH264()  { return other(MediaFormat.MIMETYPE_VIDEO_AVC); }
+    private Encoder[] otherH263()  { return other(MediaFormat.MIMETYPE_VIDEO_H263); }
+    private Encoder[] otherMpeg4() { return other(MediaFormat.MIMETYPE_VIDEO_MPEG4); }
+    private Encoder[] otherVP8()   { return other(MediaFormat.MIMETYPE_VIDEO_VP8); }
+    private Encoder[] otherVP9()   { return other(MediaFormat.MIMETYPE_VIDEO_VP9); }
+
+    private Encoder[] goog(String mime) {
+        return encoders(mime, true /* goog */);
+    }
+
+    private Encoder[] other(String mime) {
+        return encoders(mime, false /* goog */);
+    }
+
+    private Encoder[] combineArray(Encoder[] a, Encoder[] b) {
+        Encoder[] all = new Encoder[a.length + b.length];
+        System.arraycopy(a, 0, all, 0, a.length);
+        System.arraycopy(b, 0, all, a.length, b.length);
+        return all;
+    }
+
+    private Encoder[] h264()  {
+        return combineArray(googH264(), otherH264());
+    }
+
+    private Encoder[] vp8()  {
+        return combineArray(googVP8(), otherVP8());
+    }
+
+    private Encoder[] encoders(String mime, boolean goog) {
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        ArrayList<Encoder> result = new ArrayList<Encoder>();
+
+        for (MediaCodecInfo info : mcl.getCodecInfos()) {
+            if (!info.isEncoder()
+                    || info.getName().toLowerCase().startsWith("omx.google.") != goog) {
+                continue;
+            }
+            CodecCapabilities caps = null;
+            try {
+                caps = info.getCapabilitiesForType(mime);
+            } catch (IllegalArgumentException e) { // mime is not supported
+                continue;
+            }
+            assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
+            result.add(new Encoder(info.getName(), mime, caps));
+        }
+        return result.toArray(new Encoder[result.size()]);
+    }
+
+    public void testGoogH265FlexMinMin()   { minmin(googH265(),   true /* flex */); }
+    public void testGoogH265SurfMinMin()   { minmin(googH265(),   false /* flex */); }
+    public void testGoogH264FlexMinMin()   { minmin(googH264(),   true /* flex */); }
+    public void testGoogH264SurfMinMin()   { minmin(googH264(),   false /* flex */); }
+    public void testGoogH263FlexMinMin()   { minmin(googH263(),   true /* flex */); }
+    public void testGoogH263SurfMinMin()   { minmin(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexMinMin()  { minmin(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfMinMin()  { minmin(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexMinMin()    { minmin(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfMinMin()    { minmin(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexMinMin()    { minmin(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfMinMin()    { minmin(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexMinMin()  { minmin(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfMinMin()  { minmin(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexMinMin()  { minmin(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfMinMin()  { minmin(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexMinMin()  { minmin(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfMinMin()  { minmin(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexMinMin() { minmin(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfMinMin() { minmin(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexMinMin()   { minmin(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfMinMin()   { minmin(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexMinMin()   { minmin(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfMinMin()   { minmin(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexMinMax()   { minmax(googH265(),   true /* flex */); }
+    public void testGoogH265SurfMinMax()   { minmax(googH265(),   false /* flex */); }
+    public void testGoogH264FlexMinMax()   { minmax(googH264(),   true /* flex */); }
+    public void testGoogH264SurfMinMax()   { minmax(googH264(),   false /* flex */); }
+    public void testGoogH263FlexMinMax()   { minmax(googH263(),   true /* flex */); }
+    public void testGoogH263SurfMinMax()   { minmax(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexMinMax()  { minmax(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfMinMax()  { minmax(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexMinMax()    { minmax(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfMinMax()    { minmax(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexMinMax()    { minmax(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfMinMax()    { minmax(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexMinMax()  { minmax(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfMinMax()  { minmax(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexMinMax()  { minmax(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfMinMax()  { minmax(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexMinMax()  { minmax(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfMinMax()  { minmax(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexMinMax() { minmax(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfMinMax() { minmax(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexMinMax()   { minmax(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfMinMax()   { minmax(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexMinMax()   { minmax(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfMinMax()   { minmax(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexMaxMin()   { maxmin(googH265(),   true /* flex */); }
+    public void testGoogH265SurfMaxMin()   { maxmin(googH265(),   false /* flex */); }
+    public void testGoogH264FlexMaxMin()   { maxmin(googH264(),   true /* flex */); }
+    public void testGoogH264SurfMaxMin()   { maxmin(googH264(),   false /* flex */); }
+    public void testGoogH263FlexMaxMin()   { maxmin(googH263(),   true /* flex */); }
+    public void testGoogH263SurfMaxMin()   { maxmin(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexMaxMin()  { maxmin(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfMaxMin()  { maxmin(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexMaxMin()    { maxmin(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfMaxMin()    { maxmin(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexMaxMin()    { maxmin(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfMaxMin()    { maxmin(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexMaxMin()  { maxmin(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfMaxMin()  { maxmin(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexMaxMin()  { maxmin(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfMaxMin()  { maxmin(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexMaxMin()  { maxmin(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfMaxMin()  { maxmin(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexMaxMin() { maxmin(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfMaxMin() { maxmin(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexMaxMin()   { maxmin(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfMaxMin()   { maxmin(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexMaxMin()   { maxmin(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfMaxMin()   { maxmin(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexMaxMax()   { maxmax(googH265(),   true /* flex */); }
+    public void testGoogH265SurfMaxMax()   { maxmax(googH265(),   false /* flex */); }
+    public void testGoogH264FlexMaxMax()   { maxmax(googH264(),   true /* flex */); }
+    public void testGoogH264SurfMaxMax()   { maxmax(googH264(),   false /* flex */); }
+    public void testGoogH263FlexMaxMax()   { maxmax(googH263(),   true /* flex */); }
+    public void testGoogH263SurfMaxMax()   { maxmax(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexMaxMax()  { maxmax(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfMaxMax()  { maxmax(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexMaxMax()    { maxmax(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfMaxMax()    { maxmax(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexMaxMax()    { maxmax(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfMaxMax()    { maxmax(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexMaxMax()  { maxmax(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfMaxMax()  { maxmax(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexMaxMax()  { maxmax(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfMaxMax()  { maxmax(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexMaxMax()  { maxmax(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfMaxMax()  { maxmax(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexMaxMax() { maxmax(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfMaxMax() { maxmax(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexMaxMax()   { maxmax(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfMaxMax()   { maxmax(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexMaxMax()   { maxmax(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfMaxMax()   { maxmax(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexNearMinMin()   { nearminmin(googH265(),   true /* flex */); }
+    public void testGoogH265SurfNearMinMin()   { nearminmin(googH265(),   false /* flex */); }
+    public void testGoogH264FlexNearMinMin()   { nearminmin(googH264(),   true /* flex */); }
+    public void testGoogH264SurfNearMinMin()   { nearminmin(googH264(),   false /* flex */); }
+    public void testGoogH263FlexNearMinMin()   { nearminmin(googH263(),   true /* flex */); }
+    public void testGoogH263SurfNearMinMin()   { nearminmin(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexNearMinMin()  { nearminmin(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfNearMinMin()  { nearminmin(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexNearMinMin()    { nearminmin(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfNearMinMin()    { nearminmin(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexNearMinMin()    { nearminmin(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfNearMinMin()    { nearminmin(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexNearMinMin()  { nearminmin(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfNearMinMin()  { nearminmin(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexNearMinMin()  { nearminmin(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfNearMinMin()  { nearminmin(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexNearMinMin()  { nearminmin(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfNearMinMin()  { nearminmin(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexNearMinMin() { nearminmin(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfNearMinMin() { nearminmin(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexNearMinMin()   { nearminmin(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfNearMinMin()   { nearminmin(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexNearMinMin()   { nearminmin(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfNearMinMin()   { nearminmin(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexNearMinMax()   { nearminmax(googH265(),   true /* flex */); }
+    public void testGoogH265SurfNearMinMax()   { nearminmax(googH265(),   false /* flex */); }
+    public void testGoogH264FlexNearMinMax()   { nearminmax(googH264(),   true /* flex */); }
+    public void testGoogH264SurfNearMinMax()   { nearminmax(googH264(),   false /* flex */); }
+    public void testGoogH263FlexNearMinMax()   { nearminmax(googH263(),   true /* flex */); }
+    public void testGoogH263SurfNearMinMax()   { nearminmax(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexNearMinMax()  { nearminmax(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfNearMinMax()  { nearminmax(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexNearMinMax()    { nearminmax(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfNearMinMax()    { nearminmax(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexNearMinMax()    { nearminmax(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfNearMinMax()    { nearminmax(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexNearMinMax()  { nearminmax(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfNearMinMax()  { nearminmax(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexNearMinMax()  { nearminmax(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfNearMinMax()  { nearminmax(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexNearMinMax()  { nearminmax(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfNearMinMax()  { nearminmax(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexNearMinMax() { nearminmax(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfNearMinMax() { nearminmax(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexNearMinMax()   { nearminmax(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfNearMinMax()   { nearminmax(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexNearMinMax()   { nearminmax(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfNearMinMax()   { nearminmax(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexNearMaxMin()   { nearmaxmin(googH265(),   true /* flex */); }
+    public void testGoogH265SurfNearMaxMin()   { nearmaxmin(googH265(),   false /* flex */); }
+    public void testGoogH264FlexNearMaxMin()   { nearmaxmin(googH264(),   true /* flex */); }
+    public void testGoogH264SurfNearMaxMin()   { nearmaxmin(googH264(),   false /* flex */); }
+    public void testGoogH263FlexNearMaxMin()   { nearmaxmin(googH263(),   true /* flex */); }
+    public void testGoogH263SurfNearMaxMin()   { nearmaxmin(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexNearMaxMin()  { nearmaxmin(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfNearMaxMin()  { nearmaxmin(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexNearMaxMin()    { nearmaxmin(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfNearMaxMin()    { nearmaxmin(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexNearMaxMin()    { nearmaxmin(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfNearMaxMin()    { nearmaxmin(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexNearMaxMin()  { nearmaxmin(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfNearMaxMin()  { nearmaxmin(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexNearMaxMin()  { nearmaxmin(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfNearMaxMin()  { nearmaxmin(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexNearMaxMin()  { nearmaxmin(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfNearMaxMin()  { nearmaxmin(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexNearMaxMin() { nearmaxmin(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfNearMaxMin() { nearmaxmin(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexNearMaxMin()   { nearmaxmin(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfNearMaxMin()   { nearmaxmin(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexNearMaxMin()   { nearmaxmin(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfNearMaxMin()   { nearmaxmin(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexNearMaxMax()   { nearmaxmax(googH265(),   true /* flex */); }
+    public void testGoogH265SurfNearMaxMax()   { nearmaxmax(googH265(),   false /* flex */); }
+    public void testGoogH264FlexNearMaxMax()   { nearmaxmax(googH264(),   true /* flex */); }
+    public void testGoogH264SurfNearMaxMax()   { nearmaxmax(googH264(),   false /* flex */); }
+    public void testGoogH263FlexNearMaxMax()   { nearmaxmax(googH263(),   true /* flex */); }
+    public void testGoogH263SurfNearMaxMax()   { nearmaxmax(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexNearMaxMax()  { nearmaxmax(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfNearMaxMax()  { nearmaxmax(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexNearMaxMax()    { nearmaxmax(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfNearMaxMax()    { nearmaxmax(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexNearMaxMax()    { nearmaxmax(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfNearMaxMax()    { nearmaxmax(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexNearMaxMax()  { nearmaxmax(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfNearMaxMax()  { nearmaxmax(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexNearMaxMax()  { nearmaxmax(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfNearMaxMax()  { nearmaxmax(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexNearMaxMax()  { nearmaxmax(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfNearMaxMax()  { nearmaxmax(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexNearMaxMax() { nearmaxmax(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfNearMaxMax() { nearmaxmax(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexNearMaxMax()   { nearmaxmax(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfNearMaxMax()   { nearmaxmax(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexNearMaxMax()   { nearmaxmax(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfNearMaxMax()   { nearmaxmax(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexArbitraryW()   { arbitraryw(googH265(),   true /* flex */); }
+    public void testGoogH265SurfArbitraryW()   { arbitraryw(googH265(),   false /* flex */); }
+    public void testGoogH264FlexArbitraryW()   { arbitraryw(googH264(),   true /* flex */); }
+    public void testGoogH264SurfArbitraryW()   { arbitraryw(googH264(),   false /* flex */); }
+    public void testGoogH263FlexArbitraryW()   { arbitraryw(googH263(),   true /* flex */); }
+    public void testGoogH263SurfArbitraryW()   { arbitraryw(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexArbitraryW()  { arbitraryw(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfArbitraryW()  { arbitraryw(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexArbitraryW()    { arbitraryw(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfArbitraryW()    { arbitraryw(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexArbitraryW()    { arbitraryw(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfArbitraryW()    { arbitraryw(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexArbitraryW()  { arbitraryw(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfArbitraryW()  { arbitraryw(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexArbitraryW()  { arbitraryw(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfArbitraryW()  { arbitraryw(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexArbitraryW()  { arbitraryw(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfArbitraryW()  { arbitraryw(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexArbitraryW() { arbitraryw(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfArbitraryW() { arbitraryw(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexArbitraryW()   { arbitraryw(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfArbitraryW()   { arbitraryw(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexArbitraryW()   { arbitraryw(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfArbitraryW()   { arbitraryw(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexArbitraryH()   { arbitraryh(googH265(),   true /* flex */); }
+    public void testGoogH265SurfArbitraryH()   { arbitraryh(googH265(),   false /* flex */); }
+    public void testGoogH264FlexArbitraryH()   { arbitraryh(googH264(),   true /* flex */); }
+    public void testGoogH264SurfArbitraryH()   { arbitraryh(googH264(),   false /* flex */); }
+    public void testGoogH263FlexArbitraryH()   { arbitraryh(googH263(),   true /* flex */); }
+    public void testGoogH263SurfArbitraryH()   { arbitraryh(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexArbitraryH()  { arbitraryh(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfArbitraryH()  { arbitraryh(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexArbitraryH()    { arbitraryh(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfArbitraryH()    { arbitraryh(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexArbitraryH()    { arbitraryh(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfArbitraryH()    { arbitraryh(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexArbitraryH()  { arbitraryh(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfArbitraryH()  { arbitraryh(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexArbitraryH()  { arbitraryh(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfArbitraryH()  { arbitraryh(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexArbitraryH()  { arbitraryh(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfArbitraryH()  { arbitraryh(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexArbitraryH() { arbitraryh(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfArbitraryH() { arbitraryh(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexArbitraryH()   { arbitraryh(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfArbitraryH()   { arbitraryh(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexArbitraryH()   { arbitraryh(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfArbitraryH()   { arbitraryh(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexQCIF()   { specific(googH265(),   176, 144, true /* flex */); }
+    public void testGoogH265SurfQCIF()   { specific(googH265(),   176, 144, false /* flex */); }
+    public void testGoogH264FlexQCIF()   { specific(googH264(),   176, 144, true /* flex */); }
+    public void testGoogH264SurfQCIF()   { specific(googH264(),   176, 144, false /* flex */); }
+    public void testGoogH263FlexQCIF()   { specific(googH263(),   176, 144, true /* flex */); }
+    public void testGoogH263SurfQCIF()   { specific(googH263(),   176, 144, false /* flex */); }
+    public void testGoogMpeg4FlexQCIF()  { specific(googMpeg4(),  176, 144, true /* flex */); }
+    public void testGoogMpeg4SurfQCIF()  { specific(googMpeg4(),  176, 144, false /* flex */); }
+    public void testGoogVP8FlexQCIF()    { specific(googVP8(),    176, 144, true /* flex */); }
+    public void testGoogVP8SurfQCIF()    { specific(googVP8(),    176, 144, false /* flex */); }
+    public void testGoogVP9FlexQCIF()    { specific(googVP9(),    176, 144, true /* flex */); }
+    public void testGoogVP9SurfQCIF()    { specific(googVP9(),    176, 144, false /* flex */); }
+
+    public void testOtherH265FlexQCIF()  { specific(otherH265(),  176, 144, true /* flex */); }
+    public void testOtherH265SurfQCIF()  { specific(otherH265(),  176, 144, false /* flex */); }
+    public void testOtherH264FlexQCIF()  { specific(otherH264(),  176, 144, true /* flex */); }
+    public void testOtherH264SurfQCIF()  { specific(otherH264(),  176, 144, false /* flex */); }
+    public void testOtherH263FlexQCIF()  { specific(otherH263(),  176, 144, true /* flex */); }
+    public void testOtherH263SurfQCIF()  { specific(otherH263(),  176, 144, false /* flex */); }
+    public void testOtherMpeg4FlexQCIF() { specific(otherMpeg4(), 176, 144, true /* flex */); }
+    public void testOtherMpeg4SurfQCIF() { specific(otherMpeg4(), 176, 144, false /* flex */); }
+    public void testOtherVP8FlexQCIF()   { specific(otherVP8(),   176, 144, true /* flex */); }
+    public void testOtherVP8SurfQCIF()   { specific(otherVP8(),   176, 144, false /* flex */); }
+    public void testOtherVP9FlexQCIF()   { specific(otherVP9(),   176, 144, true /* flex */); }
+    public void testOtherVP9SurfQCIF()   { specific(otherVP9(),   176, 144, false /* flex */); }
+
+    public void testGoogH265Flex480p()   { specific(googH265(),   720, 480, true /* flex */); }
+    public void testGoogH265Surf480p()   { specific(googH265(),   720, 480, false /* flex */); }
+    public void testGoogH264Flex480p()   { specific(googH264(),   720, 480, true /* flex */); }
+    public void testGoogH264Surf480p()   { specific(googH264(),   720, 480, false /* flex */); }
+    public void testGoogH263Flex480p()   { specific(googH263(),   720, 480, true /* flex */); }
+    public void testGoogH263Surf480p()   { specific(googH263(),   720, 480, false /* flex */); }
+    public void testGoogMpeg4Flex480p()  { specific(googMpeg4(),  720, 480, true /* flex */); }
+    public void testGoogMpeg4Surf480p()  { specific(googMpeg4(),  720, 480, false /* flex */); }
+    public void testGoogVP8Flex480p()    { specific(googVP8(),    720, 480, true /* flex */); }
+    public void testGoogVP8Surf480p()    { specific(googVP8(),    720, 480, false /* flex */); }
+    public void testGoogVP9Flex480p()    { specific(googVP9(),    720, 480, true /* flex */); }
+    public void testGoogVP9Surf480p()    { specific(googVP9(),    720, 480, false /* flex */); }
+
+    public void testOtherH265Flex480p()  { specific(otherH265(),  720, 480, true /* flex */); }
+    public void testOtherH265Surf480p()  { specific(otherH265(),  720, 480, false /* flex */); }
+    public void testOtherH264Flex480p()  { specific(otherH264(),  720, 480, true /* flex */); }
+    public void testOtherH264Surf480p()  { specific(otherH264(),  720, 480, false /* flex */); }
+    public void testOtherH263Flex480p()  { specific(otherH263(),  720, 480, true /* flex */); }
+    public void testOtherH263Surf480p()  { specific(otherH263(),  720, 480, false /* flex */); }
+    public void testOtherMpeg4Flex480p() { specific(otherMpeg4(), 720, 480, true /* flex */); }
+    public void testOtherMpeg4Surf480p() { specific(otherMpeg4(), 720, 480, false /* flex */); }
+    public void testOtherVP8Flex480p()   { specific(otherVP8(),   720, 480, true /* flex */); }
+    public void testOtherVP8Surf480p()   { specific(otherVP8(),   720, 480, false /* flex */); }
+    public void testOtherVP9Flex480p()   { specific(otherVP9(),   720, 480, true /* flex */); }
+    public void testOtherVP9Surf480p()   { specific(otherVP9(),   720, 480, false /* flex */); }
+
+    // even though H.263 and MPEG-4 are not defined for 720p or 1080p
+    // test for it, in case device claims support for it.
+
+    public void testGoogH265Flex720p()   { specific(googH265(),   1280, 720, true /* flex */); }
+    public void testGoogH265Surf720p()   { specific(googH265(),   1280, 720, false /* flex */); }
+    public void testGoogH264Flex720p()   { specific(googH264(),   1280, 720, true /* flex */); }
+    public void testGoogH264Surf720p()   { specific(googH264(),   1280, 720, false /* flex */); }
+    public void testGoogH263Flex720p()   { specific(googH263(),   1280, 720, true /* flex */); }
+    public void testGoogH263Surf720p()   { specific(googH263(),   1280, 720, false /* flex */); }
+    public void testGoogMpeg4Flex720p()  { specific(googMpeg4(),  1280, 720, true /* flex */); }
+    public void testGoogMpeg4Surf720p()  { specific(googMpeg4(),  1280, 720, false /* flex */); }
+    public void testGoogVP8Flex720p()    { specific(googVP8(),    1280, 720, true /* flex */); }
+    public void testGoogVP8Surf720p()    { specific(googVP8(),    1280, 720, false /* flex */); }
+    public void testGoogVP9Flex720p()    { specific(googVP9(),    1280, 720, true /* flex */); }
+    public void testGoogVP9Surf720p()    { specific(googVP9(),    1280, 720, false /* flex */); }
+
+    public void testOtherH265Flex720p()  { specific(otherH265(),  1280, 720, true /* flex */); }
+    public void testOtherH265Surf720p()  { specific(otherH265(),  1280, 720, false /* flex */); }
+    public void testOtherH264Flex720p()  { specific(otherH264(),  1280, 720, true /* flex */); }
+    public void testOtherH264Surf720p()  { specific(otherH264(),  1280, 720, false /* flex */); }
+    public void testOtherH263Flex720p()  { specific(otherH263(),  1280, 720, true /* flex */); }
+    public void testOtherH263Surf720p()  { specific(otherH263(),  1280, 720, false /* flex */); }
+    public void testOtherMpeg4Flex720p() { specific(otherMpeg4(), 1280, 720, true /* flex */); }
+    public void testOtherMpeg4Surf720p() { specific(otherMpeg4(), 1280, 720, false /* flex */); }
+    public void testOtherVP8Flex720p()   { specific(otherVP8(),   1280, 720, true /* flex */); }
+    public void testOtherVP8Surf720p()   { specific(otherVP8(),   1280, 720, false /* flex */); }
+    public void testOtherVP9Flex720p()   { specific(otherVP9(),   1280, 720, true /* flex */); }
+    public void testOtherVP9Surf720p()   { specific(otherVP9(),   1280, 720, false /* flex */); }
+
+    public void testGoogH265Flex1080p()   { specific(googH265(),   1920, 1080, true /* flex */); }
+    public void testGoogH265Surf1080p()   { specific(googH265(),   1920, 1080, false /* flex */); }
+    public void testGoogH264Flex1080p()   { specific(googH264(),   1920, 1080, true /* flex */); }
+    public void testGoogH264Surf1080p()   { specific(googH264(),   1920, 1080, false /* flex */); }
+    public void testGoogH263Flex1080p()   { specific(googH263(),   1920, 1080, true /* flex */); }
+    public void testGoogH263Surf1080p()   { specific(googH263(),   1920, 1080, false /* flex */); }
+    public void testGoogMpeg4Flex1080p()  { specific(googMpeg4(),  1920, 1080, true /* flex */); }
+    public void testGoogMpeg4Surf1080p()  { specific(googMpeg4(),  1920, 1080, false /* flex */); }
+    public void testGoogVP8Flex1080p()    { specific(googVP8(),    1920, 1080, true /* flex */); }
+    public void testGoogVP8Surf1080p()    { specific(googVP8(),    1920, 1080, false /* flex */); }
+    public void testGoogVP9Flex1080p()    { specific(googVP9(),    1920, 1080, true /* flex */); }
+    public void testGoogVP9Surf1080p()    { specific(googVP9(),    1920, 1080, false /* flex */); }
+
+    public void testOtherH265Flex1080p()  { specific(otherH265(),  1920, 1080, true /* flex */); }
+    public void testOtherH265Surf1080p()  { specific(otherH265(),  1920, 1080, false /* flex */); }
+    public void testOtherH264Flex1080p()  { specific(otherH264(),  1920, 1080, true /* flex */); }
+    public void testOtherH264Surf1080p()  { specific(otherH264(),  1920, 1080, false /* flex */); }
+    public void testOtherH263Flex1080p()  { specific(otherH263(),  1920, 1080, true /* flex */); }
+    public void testOtherH263Surf1080p()  { specific(otherH263(),  1920, 1080, false /* flex */); }
+    public void testOtherMpeg4Flex1080p() { specific(otherMpeg4(), 1920, 1080, true /* flex */); }
+    public void testOtherMpeg4Surf1080p() { specific(otherMpeg4(), 1920, 1080, false /* flex */); }
+    public void testOtherVP8Flex1080p()   { specific(otherVP8(),   1920, 1080, true /* flex */); }
+    public void testOtherVP8Surf1080p()   { specific(otherVP8(),   1920, 1080, false /* flex */); }
+    public void testOtherVP9Flex1080p()   { specific(otherVP9(),   1920, 1080, true /* flex */); }
+    public void testOtherVP9Surf1080p()   { specific(otherVP9(),   1920, 1080, false /* flex */); }
+
+    // Tests encoder profiles required by CDD.
+    // H264
+    public void testH264LowQualitySDSupport()   {
+        support(h264(), 320, 240, 20, 384 * 1000);
+    }
+
+    public void testH264HighQualitySDSupport()   {
+        support(h264(), 720, 480, 30, 2 * 1000000);
+    }
+
+    public void testH264FlexQVGA20fps384kbps()   {
+        detailed(h264(), 320, 240, 20, 384 * 1000, true /* flex */);
+    }
+
+    public void testH264SurfQVGA20fps384kbps()   {
+        detailed(h264(), 320, 240, 20, 384 * 1000, false /* flex */);
+    }
+
+    public void testH264Flex480p30fps2Mbps()   {
+        detailed(h264(), 720, 480, 30, 2 * 1000000, true /* flex */);
+    }
+
+    public void testH264Surf480p30fps2Mbps()   {
+        detailed(h264(), 720, 480, 30, 2 * 1000000, false /* flex */);
+    }
+
+    public void testH264Flex720p30fps4Mbps()   {
+        detailed(h264(), 1280, 720, 30, 4 * 1000000, true /* flex */);
+    }
+
+    public void testH264Surf720p30fps4Mbps()   {
+        detailed(h264(), 1280, 720, 30, 4 * 1000000, false /* flex */);
+    }
+
+    public void testH264Flex1080p30fps10Mbps()   {
+        detailed(h264(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
+    }
+
+    public void testH264Surf1080p30fps10Mbps()   {
+        detailed(h264(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
+    }
+
+    // VP8
+    public void testVP8LowQualitySDSupport()   {
+        support(vp8(), 320, 180, 30, 800 * 1000);
+    }
+
+    public void testVP8HighQualitySDSupport()   {
+        support(vp8(), 640, 360, 30, 2 * 1000000);
+    }
+
+    public void testVP8Flex180p30fps800kbps()   {
+        detailed(vp8(), 320, 180, 30, 800 * 1000, true /* flex */);
+    }
+
+    public void testVP8Surf180p30fps800kbps()   {
+        detailed(vp8(), 320, 180, 30, 800 * 1000, false /* flex */);
+    }
+
+    public void testVP8Flex360p30fps2Mbps()   {
+        detailed(vp8(), 640, 360, 30, 2 * 1000000, true /* flex */);
+    }
+
+    public void testVP8Surf360p30fps2Mbps()   {
+        detailed(vp8(), 640, 360, 30, 2 * 1000000, false /* flex */);
+    }
+
+    public void testVP8Flex720p30fps4Mbps()   {
+        detailed(vp8(), 1280, 720, 30, 4 * 1000000, true /* flex */);
+    }
+
+    public void testVP8Surf720p30fps4Mbps()   {
+        detailed(vp8(), 1280, 720, 30, 4 * 1000000, false /* flex */);
+    }
+
+    public void testVP8Flex1080p30fps10Mbps()   {
+        detailed(vp8(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
+    }
+
+    public void testVP8Surf1080p30fps10Mbps()   {
+        detailed(vp8(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
+    }
+
+    private void minmin(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 0 /* x */, 0 /* y */, flexYUV, false /* near */);
+    }
+
+    private void minmax(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 0 /* x */, 1 /* y */, flexYUV, false /* near */);
+    }
+
+    private void maxmin(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 1 /* x */, 0 /* y */, flexYUV, false /* near */);
+    }
+
+    private void maxmax(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 1 /* x */, 1 /* y */, flexYUV, false /* near */);
+    }
+
+    private void nearminmin(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 0 /* x */, 0 /* y */, flexYUV, true /* near */);
+    }
+
+    private void nearminmax(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 0 /* x */, 1 /* y */, flexYUV, true /* near */);
+    }
+
+    private void nearmaxmin(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 1 /* x */, 0 /* y */, flexYUV, true /* near */);
+    }
+
+    private void nearmaxmax(Encoder[] encoders, boolean flexYUV) {
+        extreme(encoders, 1 /* x */, 1 /* y */, flexYUV, true /* near */);
+    }
+
+    private void extreme(Encoder[] encoders, int x, int y, boolean flexYUV, boolean near) {
+        boolean skipped = true;
+        if (encoders.length == 0) {
+            MediaUtils.skipTest("no such encoder present");
+            return;
+        }
+        for (Encoder encoder: encoders) {
+            if (encoder.testExtreme(x, y, flexYUV, near)) {
+                skipped = false;
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("duplicate resolution extreme");
+        }
+    }
+
+    private void arbitrary(Encoder[] encoders, boolean flexYUV, boolean widths) {
+        boolean skipped = true;
+        if (encoders.length == 0) {
+            MediaUtils.skipTest("no such encoder present");
+            return;
+        }
+        for (Encoder encoder: encoders) {
+            if (encoder.testArbitrary(flexYUV, widths)) {
+                skipped = false;
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("duplicate resolution");
+        }
+    }
+
+    private void arbitraryw(Encoder[] encoders, boolean flexYUV) {
+        arbitrary(encoders, flexYUV, true /* widths */);
+    }
+
+    private void arbitraryh(Encoder[] encoders, boolean flexYUV) {
+        arbitrary(encoders, flexYUV, false /* widths */);
+    }
+
+    /* test specific size */
+    private void specific(Encoder[] encoders, int width, int height, boolean flexYUV) {
+        boolean skipped = true;
+        if (encoders.length == 0) {
+            MediaUtils.skipTest("no such encoder present");
+            return;
+        }
+        for (Encoder encoder : encoders) {
+            if (encoder.testSpecific(width, height, flexYUV)) {
+                skipped = false;
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("duplicate or unsupported resolution");
+        }
+    }
+
+    /* test size, frame rate and bit rate */
+    private void detailed(
+            Encoder[] encoders, int width, int height, int frameRate, int bitRate,
+            boolean flexYUV) {
+        if (encoders.length == 0) {
+            MediaUtils.skipTest("no such encoder present");
+            return;
+        }
+        boolean skipped = true;
+        for (Encoder encoder : encoders) {
+            if (encoder.testSupport(width, height, frameRate, bitRate)) {
+                skipped = false;
+                encoder.testDetailed(width, height, frameRate, bitRate, flexYUV);
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("unsupported resolution and rate");
+        }
+    }
+
+    /* test size and rate are supported */
+    private void support(Encoder[] encoders, int width, int height, int frameRate, int bitRate) {
+        boolean supported = false;
+        if (encoders.length == 0) {
+            MediaUtils.skipTest("no such encoder present");
+            return;
+        }
+        for (Encoder encoder : encoders) {
+            if (encoder.testSupport(width, height, frameRate, bitRate)) {
+                supported = true;
+                break;
+            }
+        }
+        if (!supported) {
+            fail("unsupported format " + width + "x" + height + " " +
+                    frameRate + "fps " + bitRate + "bps");
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/VirtualizerTest.java b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
index 51adf1d..ac8eb84 100644
--- a/tests/tests/media/src/android/media/cts/VirtualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
@@ -16,14 +16,18 @@
 
 package android.media.cts;
 
+import android.content.Context;
 import android.media.audiofx.AudioEffect;
 import android.media.AudioFormat;
-import android.media.AudioManager;
 import android.media.audiofx.Virtualizer;
 import android.os.Looper;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.cts.media.R;
+
+import java.util.Arrays;
+
 public class VirtualizerTest extends PostProcTestBase {
 
     private String TAG = "VirtualizerTest";
@@ -288,6 +292,256 @@
     }
 
     //-----------------------------------------------------------------
+    // 4 virtualizer capabilities
+    //----------------------------------
+
+    //Test case 4.0: test virtualization format / mode query: at least one of the following
+    //   combinations must be supported, otherwise the effect doesn't really qualify as
+    //   a virtualizer: AudioFormat.CHANNEL_OUT_STEREO or the quad and 5.1 side/back variants,
+    //   in VIRTUALIZATION_MODE_BINAURAL or VIRTUALIZATION_MODE_TRANSAURAL
+    public void test4_0FormatModeQuery() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            boolean isAtLeastOneConfigSupported = false;
+            boolean isConfigSupported = false;
+
+            // testing combinations of input channel mask and virtualization mode
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    isConfigSupported = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    isAtLeastOneConfigSupported |= isConfigSupported;
+                    // optional logging
+                    String channelMask = Integer.toHexString(CHANNEL_MASKS[i]).toUpperCase();
+                    String nativeChannelMask =
+                            Integer.toHexString(CHANNEL_MASKS[i] >> 2).toUpperCase();
+                    Log.d(TAG, "content channel mask: 0x" + channelMask + " (native 0x"
+                            + nativeChannelMask
+                            + ") mode: " + VIRTUALIZATION_MODES[m]
+                            + " supported=" + isConfigSupported);
+                }
+            }
+
+            assertTrue("no valid configuration supported", isAtLeastOneConfigSupported);
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.1: test that the capabilities reported by Virtualizer.canVirtualize(int,int)
+    //   matches those returned by Virtualizer.getSpeakerAngles(int, int, int[])
+    public void test4_1SpeakerAnglesCapaMatchesFormatModeCapa() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            // 3: see size requirement in Virtualizer.getSpeakerAngles(int, int, int[])
+            // 6: for number of channels of 5.1 masks in CHANNEL_MASKS
+            int[] angles = new int[3*6];
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+                    boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m], angles);
+                    assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+                            canVirtualize == canGetAngles);
+                    if(canGetAngles) {
+                        //check if the number of angles matched the expected number of channels for
+                        //each CHANNEL_MASKS
+                        int expectedChannelCount = CHANNEL_MASKS_CHANNEL_COUNT[i];
+                        for(int k=0; k<expectedChannelCount; k++) {
+                            int speakerIdentification = angles[k*3];
+                            assertTrue("found unexpected speaker identification or channel count",
+                                    speakerIdentification !=AudioFormat.CHANNEL_INVALID );
+                        }
+                    }
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.2: test forcing virtualization mode: at least binaural or transaural must be
+    //   supported
+    public void test4_2VirtualizationMode() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            mVirtualizer.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+            // testing binaural
+            boolean binauralSupported =
+                    mVirtualizer.forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_BINAURAL);
+            // testing transaural
+            boolean transauralSupported = mVirtualizer
+                    .forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL);
+            // testing at least one supported
+            assertTrue("doesn't support binaural nor transaural",
+                    binauralSupported || transauralSupported);
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.3: test disabling virtualization maps to VIRTUALIZATION_MODE_OFF
+    public void test4_3DisablingVirtualizationOff() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            mVirtualizer.setEnabled(false);
+            assertFalse("invalid state from getEnabled", mVirtualizer.getEnabled());
+            int virtMode = mVirtualizer.getVirtualizationMode();
+            assertTrue("disabled virtualization isn't reported as OFF",
+                    virtMode == Virtualizer.VIRTUALIZATION_MODE_OFF);
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.4: test forcing virtualization mode to AUTO
+    public void test4_4VirtualizationModeAuto() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            mVirtualizer.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+            boolean forceRes =
+                    mVirtualizer.forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_AUTO);
+            assertTrue("can't set virtualization to AUTO", forceRes);
+
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.5: test for consistent capabilities if virtualizer is enabled or disabled
+    public void test4_5ConsistentCapabilitiesWithEnabledDisabled() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            // 3: see size requirement in Virtualizer.getSpeakerAngles(int, int, int[])
+            // 6: for number of channels of 5.1 masks in CHANNEL_MASKS
+            int[] angles = new int[3*6];
+            boolean[][] values = new boolean[VIRTUALIZATION_MODES.length][CHANNEL_MASKS.length];
+            mVirtualizer.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+                    boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m], angles);
+                    assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+                            canVirtualize == canGetAngles);
+                    values[m][i] = canVirtualize;
+                }
+            }
+
+            mVirtualizer.setEnabled(false);
+            assertTrue("invalid state from getEnabled", !mVirtualizer.getEnabled());
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+                    boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m], angles);
+                    assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+                            canVirtualize == canGetAngles);
+                    assertTrue("mismatch capability between enabled and disabled virtualizer",
+                            canVirtualize == values[m][i]);
+                }
+            }
+
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // private data
+    //----------------------------------
+    // channel masks to test at input of virtualizer
+    private static final int[] CHANNEL_MASKS = {
+            AudioFormat.CHANNEL_OUT_STEREO, //2 channels
+            AudioFormat.CHANNEL_OUT_QUAD, //4 channels
+            //AudioFormat.CHANNEL_OUT_QUAD_SIDE (definition is not public)
+            (AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+                    AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT), //4 channels
+            AudioFormat.CHANNEL_OUT_5POINT1, //6 channels
+            //AudioFormat.CHANNEL_OUT_5POINT1_SIDE (definition is not public)
+            (AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+                    AudioFormat.CHANNEL_OUT_FRONT_CENTER |
+                    AudioFormat.CHANNEL_OUT_LOW_FREQUENCY |
+                    AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT) //6 channels
+    };
+
+    private static final int[] CHANNEL_MASKS_CHANNEL_COUNT = {
+        2,
+        4,
+        4,
+        6,
+        6
+    };
+
+    private static final int[] VIRTUALIZATION_MODES = {
+            Virtualizer.VIRTUALIZATION_MODE_BINAURAL,
+            Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL
+    };
+
+    //-----------------------------------------------------------------
     // private methods
     //----------------------------------
 
@@ -362,7 +616,6 @@
                 mLooper = Looper.myLooper();
 
                 mVirtualizer2 = new Virtualizer(0, 0);
-                assertNotNull("could not create virtualizer2", mVirtualizer2);
 
                 synchronized(mLock) {
                     if (mControl) {
@@ -436,4 +689,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index 8c91e9b..3c639a0 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -16,12 +16,18 @@
 
 package android.media.cts;
 
+import com.android.cts.media.R;
+
+import android.content.Context;
 import android.media.audiofx.AudioEffect;
 import android.media.AudioFormat;
 import android.media.AudioManager;
+import android.media.MediaPlayer;
 import android.media.audiofx.Visualizer;
+import android.media.audiofx.Visualizer.MeasurementPeakRms;
 import android.os.Looper;
 import android.test.AndroidTestCase;
+import java.util.UUID;
 import android.util.Log;
 
 public class VisualizerTest extends PostProcTestBase {
@@ -126,7 +132,7 @@
     // 2 - check capture
     //----------------------------------
 
-    //Test case 2.0: test cature in polling mode
+    //Test case 2.0: test capture in polling mode
     public void test2_0PollingCapture() throws Exception {
         if (!hasAudioOutput()) {
             return;
@@ -217,6 +223,215 @@
     }
 
     //-----------------------------------------------------------------
+    // 3 - check measurement mode MEASUREMENT_MODE_NONE
+    //----------------------------------
+
+    //Test case 3.0: test setting NONE measurement mode
+    public void test3_0MeasurementModeNone() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        try {
+            getVisualizer(0);
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+
+            int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_NONE);
+            assertEquals("setMeasurementMode for NONE doesn't report success",
+                    Visualizer.SUCCESS, status);
+
+            int mode = mVisualizer.getMeasurementMode();
+            assertEquals("getMeasurementMode reports NONE",
+                    Visualizer.MEASUREMENT_MODE_NONE, mode);
+
+        } catch (IllegalStateException e) {
+            fail("method called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            releaseVisualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 4 - check measurement mode MEASUREMENT_MODE_PEAK_RMS
+    //----------------------------------
+
+    //Test case 4.0: test setting peak / RMS measurement mode
+    public void test4_0MeasurementModePeakRms() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        try {
+            getVisualizer(0);
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+
+            int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+            assertEquals("setMeasurementMode for PEAK_RMS doesn't report success",
+                    Visualizer.SUCCESS, status);
+
+            int mode = mVisualizer.getMeasurementMode();
+            assertEquals("getMeasurementMode doesn't report PEAK_RMS",
+                    Visualizer.MEASUREMENT_MODE_PEAK_RMS, mode);
+
+        } catch (IllegalStateException e) {
+            fail("method called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            releaseVisualizer();
+        }
+    }
+
+    //Test case 4.1: test measurement of peak / RMS
+    public void test4_1MeasurePeakRms() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        AudioEffect vc = null;
+        try {
+            // this test will play a 1kHz sine wave with peaks at -40dB
+            MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
+            final int EXPECTED_PEAK_MB = -4015;
+            final int EXPECTED_RMS_MB =  -4300;
+            final int MAX_MEASUREMENT_ERROR_MB = 2000;
+            assertNotNull("null MediaPlayer", mp);
+
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+                    0,
+                    mp.getAudioSessionId());
+            vc.setEnabled(true);
+
+            AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+            assertNotNull("null AudioManager", am);
+            int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+            am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                    am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
+            getVisualizer(mp.getAudioSessionId());
+            mp.setLooping(true);
+            mp.start();
+
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+            int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+            assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
+                    Visualizer.SUCCESS, status);
+            // make sure we're playing long enough so the measurement is valid
+            int currentPosition = mp.getCurrentPosition();
+            final int maxTry = 100;
+            int tryCount = 0;
+            while (currentPosition < 200 && tryCount < maxTry) {
+                Thread.sleep(50);
+                currentPosition = mp.getCurrentPosition();
+                tryCount++;
+            }
+            assertTrue("MediaPlayer not ready", tryCount < maxTry);
+
+            MeasurementPeakRms measurement = new MeasurementPeakRms();
+            status = mVisualizer.getMeasurementPeakRms(measurement);
+            mp.stop();
+            mp.release();
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
+            assertEquals("getMeasurementPeakRms() reports failure",
+                    Visualizer.SUCCESS, status);
+            Log.i("VisTest", "peak="+measurement.mPeak+"  rms="+measurement.mRms);
+            int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
+            int deltaRms =  Math.abs(measurement.mRms - EXPECTED_RMS_MB);
+            assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
+            assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
+
+        } catch (IllegalStateException e) {
+            fail("method called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            if (vc != null)
+                vc.release();
+            releaseVisualizer();
+        }
+    }
+
+    //Test case 4.2: test measurement of peak / RMS in Long MP3
+    public void test4_2MeasurePeakRmsLongMP3() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        AudioEffect vc = null;
+        try {
+            // this test will play a 1kHz sine wave with peaks at -40dB
+            MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong);
+            final int EXPECTED_PEAK_MB = -4015;
+            final int EXPECTED_RMS_MB =  -4300;
+            final int MAX_MEASUREMENT_ERROR_MB = 2000;
+            assertNotNull("null MediaPlayer", mp);
+
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+                    0,
+                    mp.getAudioSessionId());
+            vc.setEnabled(true);
+
+            AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+            assertNotNull("null AudioManager", am);
+            int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+            am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                    am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
+            getVisualizer(mp.getAudioSessionId());
+            mp.start();
+
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+            int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+            assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
+                    Visualizer.SUCCESS, status);
+            // make sure we're playing long enough so the measurement is valid
+            int currentPosition = mp.getCurrentPosition();
+            final int maxTry = 100;
+            int tryCount = 0;
+            while (currentPosition < 400 && tryCount < maxTry) {
+                Thread.sleep(50);
+                currentPosition = mp.getCurrentPosition();
+                tryCount++;
+            }
+            assertTrue("MediaPlayer not ready", tryCount < maxTry);
+
+            MeasurementPeakRms measurement = new MeasurementPeakRms();
+            status = mVisualizer.getMeasurementPeakRms(measurement);
+            mp.stop();
+            mp.release();
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
+            assertEquals("getMeasurementPeakRms() reports failure",
+                    Visualizer.SUCCESS, status);
+            Log.i("VisTest", "peak="+measurement.mPeak+"  rms="+measurement.mRms);
+            int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
+            int deltaRms =  Math.abs(measurement.mRms - EXPECTED_RMS_MB);
+            assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
+            assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
+
+        } catch (IllegalStateException e) {
+            fail("method called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            if (vc != null)
+                vc.release();
+            releaseVisualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
     // private methods
     //----------------------------------
 
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java b/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java
index 7c65824..6f4ebdd 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java
@@ -105,13 +105,18 @@
      * @throws Exception
      */
     protected void doTestVideoPlayback(int mediaNumber, int repeatCounter) throws Exception {
+        Instrumentation inst = getInstrumentation();
+        String mediaName = getFullVideoClipName(mediaNumber);
+        if (!MediaUtils.checkCodecsForPath(inst.getTargetContext(), mediaName)) {
+            return;  // not supported, message is already logged
+        }
+
         File playbackOutput = new File(WorkDir.getTopDir(), "PlaybackTestResult.txt");
         Writer output = new BufferedWriter(new FileWriter(playbackOutput, true));
 
         boolean testResult = true;
         boolean onCompleteSuccess = false;
 
-        Instrumentation inst = getInstrumentation();
         Intent intent = new Intent();
 
         intent.setClass(inst.getTargetContext(), MediaFrameworkTest.class);
@@ -119,10 +124,6 @@
 
         Activity act = inst.startActivitySync(intent);
 
-        String mediaName = getFullVideoClipName(mediaNumber);
-        if (!MediaUtils.checkCodecsForPath(inst.getTargetContext(), mediaName)) {
-            return;  // not supported, message is already logged
-        }
         for (int i = 0; i < repeatCounter; i++) {
             Log.v(TAG, "start playing " + mediaName);
             onCompleteSuccess =
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java b/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java
index 0b76185..a6a06ea 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java
@@ -32,6 +32,7 @@
 import java.io.File;
 import java.io.FileWriter;
 import java.io.Writer;
+import java.util.List;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
@@ -50,6 +51,8 @@
     private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
     private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback();
     private final static int WAIT_TIMEOUT = 10000;
+    private static final int VIDEO_WIDTH = 176;
+    private static final int VIDEO_HEIGHT = 144;
 
     private MediaRecorder mRecorder;
     private Camera mCamera;
@@ -215,11 +218,35 @@
         mSurfaceHolder = MediaFrameworkTest.getSurfaceView().getHolder();
         File stressOutFile = new File(WorkDir.getTopDir(), MEDIA_STRESS_OUTPUT);
         Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
+        int width;
+        int height;
+        Camera camera = null;
 
         if (!mHasRearCamera && !mHasFrontCamera) {
                 output.write("No camera found. Skipping recorder stress test\n");
                 return;
         }
+        // Try to get camera smallest supported resolution.
+        // If we fail for any reason, set the video size to default value.
+        try {
+            camera = Camera.open(0);
+            List<Camera.Size> previewSizes = camera.getParameters().getSupportedPreviewSizes();
+            width = previewSizes.get(0).width;
+            height = previewSizes.get(0).height;
+            for (Camera.Size size : previewSizes) {
+                if (size.width < width || size.height < height) {
+                    width = size.width;
+                    height = size.height;
+                }
+            }
+        } catch (Exception e) {
+            width = VIDEO_WIDTH;
+            height = VIDEO_HEIGHT;
+        }
+        if (camera != null) {
+            camera.release();
+        }
+        Log.v(TAG, String.format("Camera video size used for test %dx%d", width, height));
         output.write("H263 video record- reset after prepare Stress test\n");
         output.write("Total number of loops:" +
                 NUMBER_OF_RECORDER_STRESS_LOOPS + "\n");
@@ -241,7 +268,7 @@
             mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
             mRecorder.setOutputFile(filename);
             mRecorder.setVideoFrameRate(mFrameRate);
-            mRecorder.setVideoSize(176,144);
+            mRecorder.setVideoSize(width, height);
             Log.v(TAG, "setEncoder");
             mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
             mSurfaceHolder = MediaFrameworkTest.getSurfaceView().getHolder();
@@ -268,6 +295,8 @@
         }
 
         String filename;
+        int width;
+        int height;
         SurfaceHolder mSurfaceHolder;
         mSurfaceHolder = MediaFrameworkTest.getSurfaceView().getHolder();
         File stressOutFile = new File(WorkDir.getTopDir(), MEDIA_STRESS_OUTPUT);
@@ -294,6 +323,19 @@
             if (mCamera == null) {
                 break;
             }
+            // Try to get camera smallest supported resolution.
+            // If we fail for any reason, set the video size to default value.
+            List<Camera.Size> previewSizes = mCamera.getParameters().getSupportedPreviewSizes();
+            width = previewSizes.get(0).width;
+            height = previewSizes.get(0).height;
+            for (Camera.Size size : previewSizes) {
+                if (size.width < width || size.height < height) {
+                    width = size.width;
+                    height = size.height;
+                }
+            }
+            Log.v(TAG, String.format("Camera video size used for test %dx%d", width, height));
+
             mCamera.setErrorCallback(mCameraErrorCallback);
             mCamera.setPreviewDisplay(mSurfaceHolder);
             mCamera.startPreview();
@@ -315,7 +357,7 @@
             mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
             mRecorder.setOutputFile(filename);
             mRecorder.setVideoFrameRate(mFrameRate);
-            mRecorder.setVideoSize(176,144);
+            mRecorder.setVideoSize(width, height);
             Log.v(TAG, "Media recorder setEncoder");
             mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
             Log.v(TAG, "mediaRecorder setPreview");
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index d79ecdd..9daf3c4 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -29,6 +29,7 @@
 import android.net.wifi.WifiManager;
 import android.test.AndroidTestCase;
 import android.util.Log;
+import android.os.SystemProperties;
 
 import com.android.internal.telephony.PhoneConstants;
 
@@ -70,10 +71,14 @@
         // Get com.android.internal.R.array.networkAttributes
         int resId = getContext().getResources().getIdentifier("networkAttributes", "array", "android");
         String[] naStrings = getContext().getResources().getStringArray(resId);
-
+        //TODO: What is the "correct" way to determine if this is a wifi only device?
+        boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
         for (String naString : naStrings) {
             try {
                 NetworkConfig n = new NetworkConfig(naString);
+                if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) {
+                    continue;
+                }
                 mNetworks.put(n.type, n);
             } catch (Exception e) {}
         }
@@ -197,7 +202,11 @@
     }
 
     private boolean isSupported(int networkType) {
-        return mNetworks.containsKey(networkType);
+        // Change-Id I02eb5f22737720095f646f8db5c87fd66da129d6 added VPN support
+        // to all devices directly in software, independent of any external
+        // configuration.
+        return mNetworks.containsKey(networkType) ||
+               (networkType == ConnectivityManager.TYPE_VPN);
     }
 
     // true if only the system can turn it on
diff --git a/tests/tests/net/src/android/net/cts/LocalSocketTest.java b/tests/tests/net/src/android/net/cts/LocalSocketTest.java
index 0a4bc0d..70d69c8 100644
--- a/tests/tests/net/src/android/net/cts/LocalSocketTest.java
+++ b/tests/tests/net/src/android/net/cts/LocalSocketTest.java
@@ -112,6 +112,7 @@
     }
 
     public void testAccessors() throws IOException{
+        final int INITIAL_SEND_BUFFER_SIZE = 1998;
         LocalSocket socket = new LocalSocket();
         LocalSocketAddress addr = new LocalSocketAddress("secondary");
 
@@ -126,8 +127,14 @@
         socket.setReceiveBufferSize(1999);
         assertEquals(1999 << 1, socket.getReceiveBufferSize());
 
-        socket.setSendBufferSize(1998);
-        assertEquals(1998 << 1, socket.getSendBufferSize());
+        socket.setSendBufferSize(INITIAL_SEND_BUFFER_SIZE);
+        int sendBufferSize = socket.getSendBufferSize();
+        if ((INITIAL_SEND_BUFFER_SIZE << 1) < sendBufferSize) {
+            socket.setSendBufferSize(sendBufferSize / 2);
+            assertEquals(sendBufferSize, socket.getSendBufferSize());
+        } else {
+            assertEquals(INITIAL_SEND_BUFFER_SIZE << 1, sendBufferSize);
+        }
 
         // Timeout is not support at present, so set is ignored
         socket.setSoTimeout(1996);
diff --git a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
index 582b81a..eddb416 100644
--- a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
@@ -133,6 +133,7 @@
 
         // Check the response is an echo reply.
         byte[] response = new byte[bytesRead];
+        responseBuffer.flip();
         responseBuffer.get(response, 0, bytesRead);
         assertEquals((byte) 0x81, response[0]);
 
diff --git a/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java
index d434728..e132cce 100644
--- a/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java
@@ -372,8 +372,6 @@
         assertTrue(lastEvent != null);
         assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
 
-        assertTrue(eventCacheSize() == 2);
-
         // Register service again to see if we discover it
         checkForAdditionalEvents();
         clearEventCache();
@@ -405,7 +403,6 @@
                 lastEvent.mInfo.getServiceName());
 
         assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
-        assertTrue(checkCacheSize(2));
 
         checkForAdditionalEvents();
         clearEventCache();
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 3002ca3..419f320 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -29,8 +29,8 @@
 
     private static final String LOG_TAG = "BuildVersionTest";
     private static final Set<String> EXPECTED_RELEASES =
-            new HashSet<String>(Arrays.asList("5.0.1", "5.0.2"));
-    private static final int EXPECTED_SDK = 21;
+            new HashSet<String>(Arrays.asList("5.1", "5.1.1"));
+    private static final int EXPECTED_SDK = 22;
     private static final String EXPECTED_BUILD_VARIANT = "user";
     private static final String EXPECTED_TAG = "release-keys";
 
diff --git a/tests/tests/os/src/android/os/cts/StatFsTest.java b/tests/tests/os/src/android/os/cts/StatFsTest.java
index 67afde9..a0653fd 100644
--- a/tests/tests/os/src/android/os/cts/StatFsTest.java
+++ b/tests/tests/os/src/android/os/cts/StatFsTest.java
@@ -46,15 +46,15 @@
         assertTrue(stat.getBlockSize() > 0);
         assertTrue(stat.getBlockCount() > 0);
         assertTrue(stat.getFreeBlocks() >= stat.getAvailableBlocks());
-        assertTrue(stat.getAvailableBlocks() > 0);
+        assertTrue(stat.getAvailableBlocks() >= 0);
 
         assertTrue(stat.getBlockSizeLong() > 0);
         assertTrue(stat.getBlockCountLong() > 0);
         assertTrue(stat.getFreeBlocksLong() >= stat.getAvailableBlocksLong());
-        assertTrue(stat.getAvailableBlocksLong() > 0);
+        assertTrue(stat.getAvailableBlocksLong() >= 0);
 
-        assertTrue(stat.getFreeBytes() > 0);
-        assertTrue(stat.getAvailableBytes() > 0);
+        assertTrue(stat.getFreeBytes() >= 0);
+        assertTrue(stat.getAvailableBytes() >= 0);
         assertTrue(stat.getTotalBytes() > 0);
     }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
new file mode 100644
index 0000000..0c648c5
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.permission.cts;
+
+import android.content.ContentValues;
+import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for TV API related permissions.
+ */
+public class TvPermissionTest extends AndroidTestCase {
+    private static final String DUMMY_INPUT_ID = "dummy";
+
+    private boolean mHasTvInputFramework;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHasTvInputFramework = getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private void verifyQuery(Uri uri, final String[] projection,
+            String tableName) throws Exception {
+        try {
+            getContext().getContentResolver().query(uri, projection, null, null, null);
+            fail("Accessing " + tableName + " table should require READ_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyInsert(Uri uri, String tableName) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().insert(uri, values);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyUpdate(Uri uri, String tableName) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().update(uri, values, null, null);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyDelete(Uri uri, String tableName) throws Exception {
+        try {
+            getContext().getContentResolver().delete(uri, null, null);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void testQueryChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQuery(TvContract.Channels.CONTENT_URI, projection, "channels");
+    }
+
+    public void testInsertChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyInsert(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testUpdateChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyUpdate(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testDeleteChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyDelete(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testQueryPrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        final String[] projection = { TvContract.Programs._ID };
+        verifyQuery(TvContract.Programs.CONTENT_URI, projection, "programs");
+    }
+
+    public void testInsertPrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyInsert(TvContract.Programs.CONTENT_URI, "programs");
+    }
+
+    public void testUpdatePrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyUpdate(TvContract.Programs.CONTENT_URI, "programs");
+    }
+
+    public void testDeletePrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyDelete(TvContract.Programs.CONTENT_URI, "programs");
+    }
+}
diff --git a/tests/tests/permission2/src/android/permission2/cts/NoCaptureAudioOutputPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/NoCaptureAudioOutputPermissionTest.java
index b4e2855..bb63792 100644
--- a/tests/tests/permission2/src/android/permission2/cts/NoCaptureAudioOutputPermissionTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/NoCaptureAudioOutputPermissionTest.java
@@ -16,6 +16,7 @@
 
 package android.permission2.cts;
 
+import android.content.pm.PackageManager;
 import android.media.AudioFormat;
 import android.media.AudioRecord;
 import android.media.MediaRecorder.AudioSource;
@@ -37,9 +38,20 @@
      */
     @SmallTest
     public void testCreateAudioRecord() {
-        final int bufferSize = AudioRecord.getMinBufferSize(44100,
+        int bufferSize = AudioRecord.getMinBufferSize(44100,
                 AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
 
+        if (bufferSize <= 0)
+        {
+            // getMinBufferSize() returns an invalid buffer size.
+            // That could be because there is no microphone.  In that case,
+            // use this buffer size to test AudioRecord creation.
+            PackageManager packageManager = mContext.getPackageManager();
+            if (!packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+                bufferSize = 44100;
+            }
+        }
+
         // The attempt to create the AudioRecord object succeeds even if the
         // app does not have permission, but the object is not usable.
         // The API should probably throw SecurityException but it was not originally
diff --git a/tests/tests/provider/src/android/provider/cts/BrowserTest.java b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
index 2f0432a..9f96412 100644
--- a/tests/tests/provider/src/android/provider/cts/BrowserTest.java
+++ b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
@@ -84,7 +84,9 @@
                     value.put(colNames[i], cursor.getFloat(i));
                     break;
                 case Cursor.FIELD_TYPE_INTEGER:
-                    value.put(colNames[i], cursor.getLong(i));
+                    if (!"_ID".equalsIgnoreCase(colNames[i])) {
+                        value.put(colNames[i], cursor.getLong(i));
+                    }
                     break;
                 case Cursor.FIELD_TYPE_STRING:
                     value.put(colNames[i], cursor.getString(i));
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index a8f547b..c9e2213 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -61,8 +61,8 @@
     // an arbitrary int used by some tests
     private static final int SOME_ARBITRARY_INT = 143234;
 
-    // 10 sec timeout for reminder broadcast (but shouldn't usually take this long).
-    private static final int POLLING_TIMEOUT = 10000;
+    // 15 sec timeout for reminder broadcast (but shouldn't usually take this long).
+    private static final int POLLING_TIMEOUT = 15000;
 
     // @formatter:off
     private static final String[] TIME_ZONES = new String[] {
@@ -352,6 +352,7 @@
             Events.SYNC_DATA2,
             Events.SYNC_DATA3,
             Events.SYNC_DATA4,
+            Events.MUTATORS,
         };
         // @formatter:on
 
@@ -3275,6 +3276,67 @@
         CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
     }
 
+    @MediumTest
+    public void testMutatorSetCorrectly() {
+        String account = "ec_account";
+        String packageName = "com.android.cts.provider";
+        int seed = 0;
+
+        // Clean up just in case
+        CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
+
+        String mutator;
+        Cursor cursor;
+        ContentValues values = new ContentValues();
+        final long calendarId = createAndVerifyCalendar(account, seed++, null);
+
+        // Verify mutator is set to the package, via:
+        // Create:
+        final long eventId = createAndVerifyEvent(account, seed, calendarId, false, null);
+        final Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
+        cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+        cursor.moveToFirst();
+        mutator = cursor.getString(0);
+        cursor.close();
+        assertEquals(packageName, mutator);
+
+        // Edit:
+        // First clear the mutator column
+        values.clear();
+        values.putNull(Events.MUTATORS);
+        mContentResolver.update(asSyncAdapter(uri, account, CTS_TEST_TYPE), values, null, null);
+        cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+        cursor.moveToFirst();
+        mutator = cursor.getString(0);
+        cursor.close();
+        assertNull(mutator);
+        // Now edit the event and verify the mutator column
+        values.clear();
+        values.put(Events.TITLE, "New title");
+        mContentResolver.update(uri, values, null, null);
+        cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+        cursor.moveToFirst();
+        mutator = cursor.getString(0);
+        cursor.close();
+        assertEquals(packageName, mutator);
+
+        // Clean up the event
+        assertEquals(1, EventHelper.deleteEventAsSyncAdapter(mContentResolver, uri, account));
+
+        // Delete:
+        // First create as sync adapter
+        final long eventId2 = createAndVerifyEvent(account, seed, calendarId, true, null);
+        final Uri uri2 = ContentUris.withAppendedId(Events.CONTENT_URI, eventId2);
+        // Now delete the event and verify
+        values.clear();
+        values.put(Events.MUTATORS, packageName);
+        removeAndVerifyEvent(uri2, values, account);
+
+
+        // delete the calendar
+        removeAndVerifyCalendar(account, calendarId);
+    }
+
     /**
      * Acquires the set of instances that appear between the specified start and end points.
      *
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
index fcb48cf..b515550 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
@@ -29,6 +29,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.DataUsageFeedback;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
@@ -174,6 +175,57 @@
         assertCursorStoredValuesWithContactsFilter(uri, ids, false, sContentValues[1], sContentValues[0]);
     }
 
+    public void testStrequents_projection() throws Exception {
+        long[] ids = setupTestData();
+
+        // Start contact 0 and mark contact 2 as frequent
+        starContact(ids[0]);
+        markDataAsUsed(mDataIds[2], 1);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI;
+
+        DatabaseAsserts.checkProjection(mResolver, uri,
+                new String[]{
+                        Contacts._ID,
+                        Contacts.HAS_PHONE_NUMBER,
+                        Contacts.NAME_RAW_CONTACT_ID,
+                        Contacts.IS_USER_PROFILE,
+                        Contacts.CUSTOM_RINGTONE,
+                        Contacts.DISPLAY_NAME,
+                        Contacts.DISPLAY_NAME_ALTERNATIVE,
+                        Contacts.DISPLAY_NAME_SOURCE,
+                        Contacts.IN_DEFAULT_DIRECTORY,
+                        Contacts.IN_VISIBLE_GROUP,
+                        Contacts.LAST_TIME_CONTACTED,
+                        Contacts.LOOKUP_KEY,
+                        Contacts.PHONETIC_NAME,
+                        Contacts.PHONETIC_NAME_STYLE,
+                        Contacts.PHOTO_ID,
+                        Contacts.PHOTO_FILE_ID,
+                        Contacts.PHOTO_URI,
+                        Contacts.PHOTO_THUMBNAIL_URI,
+                        Contacts.SEND_TO_VOICEMAIL,
+                        Contacts.SORT_KEY_ALTERNATIVE,
+                        Contacts.SORT_KEY_PRIMARY,
+                        Contacts.STARRED,
+                        Contacts.PINNED,
+                        Contacts.TIMES_CONTACTED,
+                        Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
+                        Contacts.CONTACT_PRESENCE,
+                        Contacts.CONTACT_CHAT_CAPABILITY,
+                        Contacts.CONTACT_STATUS,
+                        Contacts.CONTACT_STATUS_TIMESTAMP,
+                        Contacts.CONTACT_STATUS_RES_PACKAGE,
+                        Contacts.CONTACT_STATUS_LABEL,
+                        Contacts.CONTACT_STATUS_ICON,
+                        Data.TIMES_USED,
+                        Data.LAST_TIME_USED,
+                },
+                new long[]{ids[0], ids[2]}
+        );
+    }
+
     public void testStrequents_phoneOnly() throws Exception {
         long[] ids = setupTestData();
 
@@ -213,6 +265,63 @@
                 sContentValues[2], sContentValues[0]);
     }
 
+    public void testStrequents_phoneOnly_projection() throws Exception {
+        long[] ids = setupTestData();
+
+        // Start contact 0 and mark contact 2 as frequent
+        starContact(ids[0]);
+        markDataAsUsed(mDataIds[2], 1);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+        DatabaseAsserts.checkProjection(mResolver, uri,
+                new String[] {
+                        Data._ID,
+                        Contacts.HAS_PHONE_NUMBER,
+                        Contacts.NAME_RAW_CONTACT_ID,
+                        Contacts.IS_USER_PROFILE,
+                        Contacts.CUSTOM_RINGTONE,
+                        Contacts.DISPLAY_NAME,
+                        Contacts.DISPLAY_NAME_ALTERNATIVE,
+                        Contacts.DISPLAY_NAME_SOURCE,
+                        Contacts.IN_DEFAULT_DIRECTORY,
+                        Contacts.IN_VISIBLE_GROUP,
+                        Contacts.LAST_TIME_CONTACTED,
+                        Contacts.LOOKUP_KEY,
+                        Contacts.PHONETIC_NAME,
+                        Contacts.PHONETIC_NAME_STYLE,
+                        Contacts.PHOTO_ID,
+                        Contacts.PHOTO_FILE_ID,
+                        Contacts.PHOTO_URI,
+                        Contacts.PHOTO_THUMBNAIL_URI,
+                        Contacts.SEND_TO_VOICEMAIL,
+                        Contacts.SORT_KEY_ALTERNATIVE,
+                        Contacts.SORT_KEY_PRIMARY,
+                        Contacts.STARRED,
+                        Contacts.PINNED,
+                        Contacts.TIMES_CONTACTED,
+                        Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
+                        Contacts.CONTACT_PRESENCE,
+                        Contacts.CONTACT_CHAT_CAPABILITY,
+                        Contacts.CONTACT_STATUS,
+                        Contacts.CONTACT_STATUS_TIMESTAMP,
+                        Contacts.CONTACT_STATUS_RES_PACKAGE,
+                        Contacts.CONTACT_STATUS_LABEL,
+                        Contacts.CONTACT_STATUS_ICON,
+                        Data.TIMES_USED,
+                        Data.LAST_TIME_USED,
+                        Phone.NUMBER,
+                        Phone.TYPE,
+                        Phone.LABEL,
+                        Phone.IS_SUPER_PRIMARY,
+                        Phone.CONTACT_ID,
+                },
+                new long[] {mDataIds[0], mDataIds[2]} // Note _id from phone_only is data._id
+        );
+    }
+
     public void testFrequents_noFrequentsReturnsEmptyCursor() throws Exception {
         long[] ids = setupTestData();
         assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_FREQUENT_URI, ids, false);
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index 0eae82b..c1b2229 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -373,7 +373,7 @@
         values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
         Uri fileUri = mResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
         // give media provider some time to realize there's no album art
-        //SystemClock.sleep(1000);
+        SystemClock.sleep(1000);
         // get its album id
         Cursor c = mResolver.query(fileUri, new String[] { MediaStore.Audio.Media.ALBUM_ID},
                 null, null, null);
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
index 7546066..7f98284 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
@@ -21,6 +21,8 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract.Contacts;
 import android.provider.cts.contacts.account.StaticAccountAuthenticator;
 import android.test.MoreAsserts;
 
@@ -261,4 +263,76 @@
         }
         return true;
     }
+
+    public static String buildIdSelection(long[] ids) {
+        StringBuilder selection = new StringBuilder();
+        selection.append(BaseColumns._ID + " in ");
+        selection.append("(");
+        for (int i = 0; i < ids.length; i++) {
+            if (i != 0) selection.append(",");
+            selection.append(ids[i]);
+        }
+        selection.append(")");
+        return selection.toString();
+    }
+
+    /**
+     * Check if a query accepts all columns in the projection, and a resulting cursor contains
+     * all expected columns.  This also checks the number of resulting rows, but don't check
+     * actual content in a returned cursor.
+     */
+    public static void checkProjection(ContentResolver resolver,
+            Uri uri, String[] allColumns, long[] ids) {
+        final String selection = buildIdSelection(ids);
+
+        // First, check the null projection (i.e. all columns).
+        checkProjectionInner(resolver, uri, null, allColumns, selection, ids.length,
+                /* keepPosition =*/ false);
+
+        // All columns.
+        checkProjectionInner(resolver, uri, allColumns, allColumns, selection, ids.length,
+                /* keepPosition =*/ true);
+
+        // Select each column one by one.
+        for (int i = 0; i < allColumns.length; i++) {
+            final String[] columns = new String[] {allColumns[i]};
+            checkProjectionInner(resolver, uri, columns, columns, selection, ids.length,
+                        /* keepPosition =*/ true);
+        }
+
+        // Select two columns.
+        for (int i = 0; i < allColumns.length; i++) {
+            for (int j = 0; j < allColumns.length; j++) {
+                // Requesting the same column multiple times is okay, but it'd make the column
+                // order check harder.
+                if (i == j) continue;
+                final String[] columns = new String[] {allColumns[i], allColumns[j]};
+                checkProjectionInner(resolver, uri, columns, columns, selection, ids.length,
+                        /* keepPosition =*/ true);
+            }
+        }
+    }
+
+    private static void checkProjectionInner(ContentResolver resolver,
+            Uri uri, String[] projection, String[] expectedColumns,
+            String selection, int expectedRowCount, boolean keepPosition) {
+        final Cursor c = resolver.query(uri, projection, selection,
+                /* args =*/ null, /* sort =*/ null);
+        Assert.assertNotNull(c);
+        try {
+            Assert.assertEquals("# of rows", expectedRowCount, c.getCount());
+
+            // Make sure expected columns exist.
+            for (int i = 0; i < expectedColumns.length; i++) {
+                final String column = expectedColumns[i];
+                if (keepPosition) {
+                    Assert.assertEquals(column, c.getColumnName(i));
+                } else {
+                    Assert.assertTrue(column, c.getColumnIndex(column) >= 0);
+                }
+            }
+        } finally {
+            c.close();
+        }
+    }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
index 1880132..8faeb22 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
@@ -24,11 +24,17 @@
         float cf1[] = {0.f, 0.f, 0.f,  0.f, 1.f, 0.f,  0.f, 0.f, 0.f};
         float cf2[] = {0.f, -1.f, 0.f,  -1.f, 5.f, -1.f,  0.f, -1.f, 0.f};
 
+        float irCoeff1 = 3.1415927f;
+        float irCoeff2 = -irCoeff1;
+        float cf3[] = {0.f, irCoeff1, 0.f,  irCoeff2, 1.f, irCoeff2,  0.f, irCoeff1, 0.f};
+
         Element e = makeElement(dt, vecSize);
 
         System.gc();
         makeBuffers(w, h, e);
 
+        mVerify.set_gAllowedIntError(1);
+
         ScriptIntrinsicConvolve3x3 si = ScriptIntrinsicConvolve3x3.create(mRS, e);
         si.setCoefficients(cf1);
         si.setInput(mAllocSrc);
@@ -116,6 +122,44 @@
         }
         //android.util.Log.e("RSI test", "test convolve U8_" + vecSize + " 2 " + w + ", " + h);
         mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+
+        si.setCoefficients(cf3);
+        sr.set_gCoeffs(cf3);
+        si.forEach(mAllocRef, sc);
+        if (dt == Element.DataType.UNSIGNED_8) {
+            switch(vecSize) {
+            case 4:
+                sr.forEach_convolve_U4(mAllocDst, sc);
+                break;
+            case 3:
+                sr.forEach_convolve_U3(mAllocDst, sc);
+                break;
+            case 2:
+                sr.forEach_convolve_U2(mAllocDst, sc);
+                break;
+            case 1:
+                sr.forEach_convolve_U1(mAllocDst, sc);
+                break;
+            }
+        } else {
+            switch(vecSize) {
+            case 4:
+                sr.forEach_convolve_F4(mAllocDst, sc);
+                break;
+            case 3:
+                sr.forEach_convolve_F3(mAllocDst, sc);
+                break;
+            case 2:
+                sr.forEach_convolve_F2(mAllocDst, sc);
+                break;
+            case 1:
+                sr.forEach_convolve_F1(mAllocDst, sc);
+                break;
+            }
+        }
+        //android.util.Log.e("RSI test", "test convolve U8_" + vecSize + " 2 " + w + ", " + h);
+        mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+
         mRS.finish();
     }
 
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
index ee92651..0753c62 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
@@ -85,13 +85,24 @@
                        -1.f,  0.f,  0.f,  0.f, -1.f,
                        -1.f, -1.f, -1.f, -1.f, -1.f};
 
+        float irCoeff1 = 3.1415927f;
+        float irCoeff2 = -irCoeff1;
+        float cf3[] = {irCoeff1, -1.f, -1.f, -1.f, irCoeff2,
+                       irCoeff1,  0.f,  0.f,  0.f, irCoeff2,
+                       irCoeff1,  0.f,  7.f,  0.f, irCoeff2,
+                       irCoeff1,  0.f,  0.f,  0.f, irCoeff2,
+                       irCoeff1, -1.f, -1.f, -1.f, irCoeff2};
+
         Element e = makeElement(dt, vecSize);
         makeBuffers(w, h, e);
 
+        mVerify.set_gAllowedIntError(1);
+
         ScriptIntrinsicConvolve5x5 si = ScriptIntrinsicConvolve5x5.create(mRS, e);
         ScriptC_intrinsic_convolve5x5 sr = new ScriptC_intrinsic_convolve5x5(mRS);
         test5(sr, si, e, cf1, "test convolve", 1, w, h, sc);
         test5(sr, si, e, cf2, "test convolve", 2, w, h, sc);
+        test5(sr, si, e, cf3, "test convolve", 3, w, h, sc);
     }
 
     public void test_U8_4() {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java
new file mode 100644
index 0000000..d593bff
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript.cts;
+
+import android.renderscript.*;
+import android.util.Log;
+
+public class IntrinsicResize extends IntrinsicBase {
+
+    static final int inX = 307;
+    static final int inY = 157;
+
+    private void testReszie(int w, int h, Element.DataType dt, int vecSize, float scaleX, float scaleY) {
+
+        Element e = makeElement(dt, vecSize);
+
+        System.gc();
+        makeSource(w, h, e);
+
+        int outW = (int) (w*scaleX);
+        int outH = (int) (h*scaleY);
+        if (mAllocRef != null) {
+            mAllocRef.destroy();
+        }
+        if (mAllocDst != null) {
+            mAllocDst.destroy();
+        }
+        mAllocRef = makeAllocation(outW, outH, e);
+        mAllocDst = makeAllocation(outW, outH, e);
+
+        ScriptIntrinsicResize si = ScriptIntrinsicResize.create(mRS);
+        si.setInput(mAllocSrc);
+        si.forEach_bicubic(mAllocRef);
+
+        ScriptC_intrinsic_resize sr = new ScriptC_intrinsic_resize(mRS);
+        sr.set_scaleX((float)w/outW);
+        sr.set_scaleY((float)h/outH);
+        sr.set_gIn(mAllocSrc);
+        sr.set_gWidthIn(w);
+        sr.set_gHeightIn(h);
+        if (dt == Element.DataType.UNSIGNED_8) {
+            switch(vecSize) {
+            case 4:
+                sr.forEach_bicubic_U4(mAllocDst);
+                break;
+            case 3:
+                sr.forEach_bicubic_U3(mAllocDst);
+                break;
+            case 2:
+                sr.forEach_bicubic_U2(mAllocDst);
+                break;
+            case 1:
+                sr.forEach_bicubic_U1(mAllocDst);
+                break;
+            }
+        }
+
+        mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+        if (outW == w && outH == h) {
+            //when scale = 1, check with the original.
+            mVerify.invoke_verify(mAllocRef, mAllocSrc, mAllocSrc);
+            mVerify.invoke_verify(mAllocDst, mAllocSrc, mAllocSrc);
+        }
+        mRS.finish();
+    }
+
+
+    public void test_U8_4_SCALE10_10_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f);
+        checkError();
+    }
+    public void test_U8_3_SCALE10_10_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f);
+        checkError();
+    }
+    public void test_U8_2_SCALE10_10_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f);
+        checkError();
+    }
+    public void test_U8_1_SCALE10_10_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f);
+        checkError();
+    }
+
+    public void test_U8_4_SCALE20_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f);
+        checkError();
+    }
+    public void test_U8_3_SCALE20_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f);
+        checkError();
+    }
+    public void test_U8_2_SCALE20_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f);
+        checkError();
+    }
+    public void test_U8_1_SCALE20_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f);
+        checkError();
+    }
+
+    public void test_U8_4_SCALE05_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f);
+        checkError();
+    }
+    public void test_U8_3_SCALE05_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f);
+        checkError();
+    }
+    public void test_U8_2_SCALE05_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f);
+        checkError();
+    }
+    public void test_U8_1_SCALE05_20_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f);
+        checkError();
+    }
+
+    public void test_U8_4_SCALE20_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f);
+        checkError();
+    }
+    public void test_U8_3_SCALE20_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f);
+        checkError();
+    }
+    public void test_U8_2_SCALE20_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f);
+        checkError();
+    }
+    public void test_U8_1_SCALE20_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f);
+        checkError();
+    }
+
+    public void test_U8_4_SCALE05_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f);
+        checkError();
+    }
+    public void test_U8_3_SCALE05_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f);
+        checkError();
+    }
+    public void test_U8_2_SCALE05_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f);
+        checkError();
+    }
+    public void test_U8_1_SCALE05_05_inSqure() {
+        testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f);
+        checkError();
+    }
+
+    public void test_U8_4_SCALE10_10_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f);
+        checkError();
+    }
+    public void test_U8_3_SCALE10_10_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f);
+        checkError();
+    }
+    public void test_U8_2_SCALE10_10_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f);
+        checkError();
+    }
+    public void test_U8_1_SCALE10_10_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f);
+        checkError();
+    }
+
+    public void test_U8_4_SCALE20_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f);
+        checkError();
+    }
+    public void test_U8_3_SCALE20_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f);
+        checkError();
+    }
+    public void test_U8_2_SCALE20_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f);
+        checkError();
+    }
+    public void test_U8_1_SCALE20_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f);
+        checkError();
+    }
+
+    public void test_U8_4_SCALE05_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f);
+        checkError();
+    }
+    public void test_U8_3_SCALE05_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f);
+        checkError();
+    }
+    public void test_U8_2_SCALE05_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f);
+        checkError();
+    }
+    public void test_U8_1_SCALE05_20_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f);
+        checkError();
+    }
+    
+    public void test_U8_4_SCALE20_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f);
+        checkError();
+    }
+    public void test_U8_3_SCALE20_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f);
+        checkError();
+    }
+    public void test_U8_2_SCALE20_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f);
+        checkError();
+    }
+    public void test_U8_1_SCALE20_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f);
+        checkError();
+    }
+    
+    public void test_U8_4_SCALE05_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f);
+        checkError();
+    }
+    public void test_U8_3_SCALE05_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f);
+        checkError();
+    }
+    public void test_U8_2_SCALE05_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f);
+        checkError();
+    }
+    public void test_U8_1_SCALE05_05_inRectangle() {
+        testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f);
+        checkError();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
new file mode 100644
index 0000000..a2d22d9
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
@@ -0,0 +1,371 @@
+ /*
+ * 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.renderscript.cts;
+
+import android.renderscript.*;
+
+public class TestVLoad extends RSBaseCompute {
+
+    private ScriptC_vload script;
+    private ScriptC_vload_relaxed scriptRelaxed;
+    Allocation walkAlloc;
+    Allocation inAlloc;
+    Allocation outAlloc;
+    private static java.util.Random random = new java.util.Random();
+
+    final int w = 253;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        random.setSeed(10);
+        script = new ScriptC_vload(mRS);
+        scriptRelaxed = new ScriptC_vload_relaxed(mRS);
+    }
+
+
+
+    protected void createWalk() {
+        int tmp[] = new int[w];
+        boolean b[] = new boolean[w];
+        int toCopy = w;
+        int i = 0;
+
+        while (toCopy > 0) {
+            int x = random.nextInt(w);
+
+            //android.util.Log.v("rs", "x " + x + ", y " + y + ", toCopy " + toCopy);
+            while ((x < w) && b[x]) {
+                x++;
+                if (x >= w) {
+                    x = 0;
+                }
+            }
+
+            int maxsize = 1;
+            b[x] = true;
+            if ((x+1 < w) && !b[x+1]) {
+                maxsize ++;
+                b[x+1] = true;
+                if ((x+2 < w) && !b[x+2]) {
+                    maxsize ++;
+                    b[x+2] = true;
+                    if ((x+3 < w) && !b[x+3]) {
+                        maxsize ++;
+                        b[x+3] = true;
+                    }
+                }
+            }
+
+            toCopy -= maxsize;
+            tmp[i] = x | (maxsize << 16);
+            android.util.Log.v("rs", "x " + x + ", vec " + maxsize);
+            i++;
+        }
+
+        walkAlloc = Allocation.createSized(mRS, Element.I32(mRS), i);
+        walkAlloc.copy1DRangeFrom(0, i, tmp);
+    }
+
+    private void testSetup(Type t) {
+        createWalk();
+
+        inAlloc = Allocation.createTyped(mRS, t);
+        outAlloc = Allocation.createTyped(mRS, t);
+        script.set_gAllocIn(inAlloc);
+        script.set_gAllocOut(outAlloc);
+        scriptRelaxed.set_gAllocIn(inAlloc);
+        scriptRelaxed.set_gAllocOut(outAlloc);
+    }
+
+    private void verify(byte[] a1, byte[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(short[] a1, short[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(int[] a1, int[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(long[] a1, long[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(float[] a1, float[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(double[] a1, double[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private byte[] randomByteArray(int len) {
+        byte t[] = new byte[len];
+        random.nextBytes(t);
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    private short[] randomShortArray(int len) {
+        short t[] = new short[len];
+        for (int i = 0; i < t.length; i++) {
+            t[i] = (short)(random.nextInt() & 0xffff);
+        }
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    private int[] randomIntArray(int len) {
+        int t[] = new int[len];
+        for (int i = 0; i < t.length; i++) {
+            t[i] = random.nextInt();
+        }
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    private long[] randomLongArray(int len) {
+        long t[] = new long[len];
+        for (int i = 0; i < t.length; i++) {
+            t[i] = random.nextLong();
+        }
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    public void testVload_char() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        script.forEach_copy2d_char(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch char: ");
+    }
+
+    public void testVload_uchar() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        script.forEach_copy2d_uchar(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch uchar: ");
+    }
+
+    public void testVload_char_relaxed() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        scriptRelaxed.forEach_copy2d_char(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed char: ");
+    }
+
+    public void testVload_uchar_relaxed() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        scriptRelaxed.forEach_copy2d_uchar(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed uchar: ");
+    }
+
+    public void testVload_short() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        script.forEach_copy2d_short(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch short: ");
+    }
+
+    public void testVload_ushort() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        script.forEach_copy2d_ushort(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ushort: ");
+    }
+
+    public void testVload_short_relaxed() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        scriptRelaxed.forEach_copy2d_short(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed short: ");
+    }
+
+    public void testVload_ushort_relaxed() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        scriptRelaxed.forEach_copy2d_ushort(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ushort: ");
+    }
+
+    public void testVload_int() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        script.forEach_copy2d_int(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch int: ");
+    }
+
+    public void testVload_uint() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        script.forEach_copy2d_uint(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch uint: ");
+    }
+
+    public void testVload_int_relaxed() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        scriptRelaxed.forEach_copy2d_int(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed int: ");
+    }
+
+    public void testVload_uint_relaxed() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        scriptRelaxed.forEach_copy2d_uint(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch uint: ");
+    }
+
+    public void testVload_long() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        script.forEach_copy2d_long(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch long: ");
+    }
+
+    public void testVload_ulong() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        script.forEach_copy2d_ulong(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ulong: ");
+    }
+    public void testVload_long_relaxed() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        scriptRelaxed.forEach_copy2d_long(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed long: ");
+    }
+    public void testVload_ulong_relaxed() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        scriptRelaxed.forEach_copy2d_ulong(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ulong: ");
+    }
+
+    public void testVload_float() {
+        testSetup(Type.createX(mRS, Element.F32(mRS), w));
+        float tmp[] = new float[w];
+        float tmp2[] = new float[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextFloat();
+        }
+        inAlloc.copyFrom(tmp);
+        script.forEach_copy2d_float(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch float: ");
+    }
+
+    public void testVload_float_relaxed() {
+        testSetup(Type.createX(mRS, Element.F32(mRS), w));
+        float tmp[] = new float[w];
+        float tmp2[] = new float[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextFloat();
+        }
+        inAlloc.copyFrom(tmp);
+        scriptRelaxed.forEach_copy2d_float(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed float: ");
+    }
+
+    public void testVload_double() {
+        testSetup(Type.createX(mRS, Element.F64(mRS), w));
+        double tmp[] = new double[w];
+        double tmp2[] = new double[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextDouble();
+        }
+        inAlloc.copyFrom(tmp);
+        script.forEach_copy2d_double(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch double: ");
+    }
+
+    public void testVload_double_relaxed() {
+        testSetup(Type.createX(mRS, Element.F64(mRS), w));
+        double tmp[] = new double[w];
+        double tmp2[] = new double[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextDouble();
+        }
+        inAlloc.copyFrom(tmp);
+        scriptRelaxed.forEach_copy2d_double(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed double: ");
+    }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
index 9f9aa2b..966dbdd 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
@@ -66,7 +66,7 @@
               + convert_float4(rsGetElementAt_uchar4(gIn, x3, y4)) * gCoeffs[23]
               + convert_float4(rsGetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24];
 
-    p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+    p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
     return convert_uchar4(p0);
 }
 
@@ -113,7 +113,7 @@
               + convert_float3(rsGetElementAt_uchar3(gIn, x3, y4)) * gCoeffs[23]
               + convert_float3(rsGetElementAt_uchar3(gIn, x4, y4)) * gCoeffs[24];
 
-    p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+    p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
     return convert_uchar3(p0);
 }
 
@@ -160,7 +160,7 @@
               + convert_float2(rsGetElementAt_uchar2(gIn, x3, y4)) * gCoeffs[23]
               + convert_float2(rsGetElementAt_uchar2(gIn, x4, y4)) * gCoeffs[24];
 
-    p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+    p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
     return convert_uchar2(p0);
 }
 
@@ -207,7 +207,7 @@
              + (float)(rsGetElementAt_uchar(gIn, x3, y4)) * gCoeffs[23]
              + (float)(rsGetElementAt_uchar(gIn, x4, y4)) * gCoeffs[24];
 
-    return clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+    return clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
 }
 
 float4 __attribute__((kernel)) convolve_F4(uint32_t x, uint32_t y) {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_resize.rs b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_resize.rs
new file mode 100644
index 0000000..fa8c8dd
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_resize.rs
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shared.rsh"
+
+int32_t gWidthIn;
+int32_t gHeightIn;
+rs_allocation gIn;
+float scaleX;
+float scaleY;
+
+static float4 cubicInterpolate_F4 (float4 p0,float4 p1,float4 p2,float4 p3 , float x) {
+    return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+            + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+static float3 cubicInterpolate_F3 (float3 p0,float3 p1,float3 p2,float3 p3 , float x) {
+    return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+            + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+static float2 cubicInterpolate_F2 (float2 p0,float2 p1,float2 p2,float2 p3 , float x) {
+    return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+            + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+static float cubicInterpolate_F1 (float p0,float p1,float p2,float p3 , float x) {
+    return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+            + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+uchar4 __attribute__((kernel)) bicubic_U4(uint32_t x, uint32_t y) {
+    float xf = (x + 0.5f) * scaleX - 0.5f;
+    float yf = (y + 0.5f) * scaleY - 0.5f;
+
+    int startx = (int) floor(xf - 1);
+    int starty = (int) floor(yf - 1);
+    xf = xf - floor(xf);
+    yf = yf - floor(yf);
+    int maxx = gWidthIn - 1;
+    int maxy = gHeightIn - 1;
+
+    uint32_t xs0 = (uint32_t) max(0, startx + 0);
+    uint32_t xs1 = (uint32_t) max(0, startx + 1);
+    uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+    uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+    uint32_t ys0 = (uint32_t) max(0, starty + 0);
+    uint32_t ys1 = (uint32_t) max(0, starty + 1);
+    uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+    uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+    float4 p00 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys0));
+    float4 p01 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys0));
+    float4 p02 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys0));
+    float4 p03 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys0));
+    float4 p0  = cubicInterpolate_F4(p00, p01, p02, p03, xf);
+
+    float4 p10 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys1));
+    float4 p11 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys1));
+    float4 p12 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys1));
+    float4 p13 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys1));
+    float4 p1  = cubicInterpolate_F4(p10, p11, p12, p13, xf);
+
+    float4 p20 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys2));
+    float4 p21 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys2));
+    float4 p22 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys2));
+    float4 p23 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys2));
+    float4 p2  = cubicInterpolate_F4(p20, p21, p22, p23, xf);
+
+    float4 p30 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys3));
+    float4 p31 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys3));
+    float4 p32 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys3));
+    float4 p33 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys3));
+    float4 p3  = cubicInterpolate_F4(p30, p31, p32, p33, xf);
+
+    float4 p  = cubicInterpolate_F4(p0, p1, p2, p3, yf);
+    p = clamp(p + 0.5f, 0.f, 255.f);
+    return convert_uchar4(p);
+}
+
+uchar3 __attribute__((kernel)) bicubic_U3(uint32_t x, uint32_t y) {
+    float xf = (x + 0.5f) * scaleX - 0.5f;
+    float yf = (y + 0.5f) * scaleY - 0.5f;
+
+    int startx = (int) floor(xf - 1);
+    int starty = (int) floor(yf - 1);
+    xf = xf - floor(xf);
+    yf = yf - floor(yf);
+    int maxx = gWidthIn - 1;
+    int maxy = gHeightIn - 1;
+
+    uint32_t xs0 = (uint32_t) max(0, startx + 0);
+    uint32_t xs1 = (uint32_t) max(0, startx + 1);
+    uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+    uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+    uint32_t ys0 = (uint32_t) max(0, starty + 0);
+    uint32_t ys1 = (uint32_t) max(0, starty + 1);
+    uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+    uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+    float3 p00 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys0));
+    float3 p01 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys0));
+    float3 p02 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys0));
+    float3 p03 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys0));
+    float3 p0  = cubicInterpolate_F3(p00, p01, p02, p03, xf);
+
+    float3 p10 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys1));
+    float3 p11 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys1));
+    float3 p12 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys1));
+    float3 p13 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys1));
+    float3 p1  = cubicInterpolate_F3(p10, p11, p12, p13, xf);
+
+    float3 p20 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys2));
+    float3 p21 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys2));
+    float3 p22 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys2));
+    float3 p23 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys2));
+    float3 p2  = cubicInterpolate_F3(p20, p21, p22, p23, xf);
+
+    float3 p30 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys3));
+    float3 p31 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys3));
+    float3 p32 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys3));
+    float3 p33 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys3));
+    float3 p3  = cubicInterpolate_F3(p30, p31, p32, p33, xf);
+
+    float3 p  = cubicInterpolate_F3(p0, p1, p2, p3, yf);
+    p = clamp(p + 0.5f, 0.f, 255.f);
+    return convert_uchar3(p);
+}
+
+uchar2 __attribute__((kernel)) bicubic_U2(uint32_t x, uint32_t y) {
+    float xf = (x + 0.5f) * scaleX - 0.5f;
+    float yf = (y + 0.5f) * scaleY - 0.5f;
+
+    int startx = (int) floor(xf - 1);
+    int starty = (int) floor(yf - 1);
+    xf = xf - floor(xf);
+    yf = yf - floor(yf);
+    int maxx = gWidthIn - 1;
+    int maxy = gHeightIn - 1;
+
+    uint32_t xs0 = (uint32_t) max(0, startx + 0);
+    uint32_t xs1 = (uint32_t) max(0, startx + 1);
+    uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+    uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+    uint32_t ys0 = (uint32_t) max(0, starty + 0);
+    uint32_t ys1 = (uint32_t) max(0, starty + 1);
+    uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+    uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+    float2 p00 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys0));
+    float2 p01 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys0));
+    float2 p02 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys0));
+    float2 p03 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys0));
+    float2 p0  = cubicInterpolate_F2(p00, p01, p02, p03, xf);
+
+    float2 p10 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys1));
+    float2 p11 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys1));
+    float2 p12 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys1));
+    float2 p13 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys1));
+    float2 p1  = cubicInterpolate_F2(p10, p11, p12, p13, xf);
+
+    float2 p20 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys2));
+    float2 p21 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys2));
+    float2 p22 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys2));
+    float2 p23 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys2));
+    float2 p2  = cubicInterpolate_F2(p20, p21, p22, p23, xf);
+
+    float2 p30 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys3));
+    float2 p31 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys3));
+    float2 p32 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys3));
+    float2 p33 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys3));
+    float2 p3  = cubicInterpolate_F2(p30, p31, p32, p33, xf);
+
+    float2 p  = cubicInterpolate_F2(p0, p1, p2, p3, yf);
+    p = clamp(p + 0.5f, 0.f, 255.f);
+    return convert_uchar2(p);
+}
+
+uchar __attribute__((kernel)) bicubic_U1(uint32_t x, uint32_t y) {
+    float xf = (x + 0.5f) * scaleX - 0.5f;
+    float yf = (y + 0.5f) * scaleY - 0.5f;
+
+    int startx = (int) floor(xf - 1);
+    int starty = (int) floor(yf - 1);
+    xf = xf - floor(xf);
+    yf = yf - floor(yf);
+    int maxx = gWidthIn - 1;
+    int maxy = gHeightIn - 1;
+
+    uint32_t xs0 = (uint32_t) max(0, startx + 0);
+    uint32_t xs1 = (uint32_t) max(0, startx + 1);
+    uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+    uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+    uint32_t ys0 = (uint32_t) max(0, starty + 0);
+    uint32_t ys1 = (uint32_t) max(0, starty + 1);
+    uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+    uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+    float p00 = (float)(rsGetElementAt_uchar(gIn, xs0, ys0));
+    float p01 = (float)(rsGetElementAt_uchar(gIn, xs1, ys0));
+    float p02 = (float)(rsGetElementAt_uchar(gIn, xs2, ys0));
+    float p03 = (float)(rsGetElementAt_uchar(gIn, xs3, ys0));
+    float p0  = cubicInterpolate_F1(p00, p01, p02, p03, xf);
+
+    float p10 = (float)(rsGetElementAt_uchar(gIn, xs0, ys1));
+    float p11 = (float)(rsGetElementAt_uchar(gIn, xs1, ys1));
+    float p12 = (float)(rsGetElementAt_uchar(gIn, xs2, ys1));
+    float p13 = (float)(rsGetElementAt_uchar(gIn, xs3, ys1));
+    float p1  = cubicInterpolate_F1(p10, p11, p12, p13, xf);
+
+    float p20 = (float)(rsGetElementAt_uchar(gIn, xs0, ys2));
+    float p21 = (float)(rsGetElementAt_uchar(gIn, xs1, ys2));
+    float p22 = (float)(rsGetElementAt_uchar(gIn, xs2, ys2));
+    float p23 = (float)(rsGetElementAt_uchar(gIn, xs3, ys2));
+    float p2  = cubicInterpolate_F1(p20, p21, p22, p23, xf);
+
+    float p30 = (float)(rsGetElementAt_uchar(gIn, xs0, ys3));
+    float p31 = (float)(rsGetElementAt_uchar(gIn, xs1, ys3));
+    float p32 = (float)(rsGetElementAt_uchar(gIn, xs2, ys3));
+    float p33 = (float)(rsGetElementAt_uchar(gIn, xs3, ys3));
+    float p3  = cubicInterpolate_F1(p30, p31, p32, p33, xf);
+
+    float p  = cubicInterpolate_F1(p0, p1, p2, p3, yf);
+    p = clamp(p + 0.5f, 0.f, 255.f);
+    return (uchar)p;
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
new file mode 100644
index 0000000..cdf5fd1
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
@@ -0,0 +1,60 @@
+ /*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty)                                                 \
+    void __attribute__((kernel)) copy2d_##ty(int xy_v) {            \
+        int lx = xy_v & 0xff;                                       \
+        int vecsize = (xy_v & 0xff0000) >> 16;                      \
+        switch(vecsize) {                                           \
+        case 1: {                                                   \
+                ty i = rsGetElementAt_##ty(gAllocIn, lx);           \
+                rsSetElementAt_##ty(gAllocOut, i, lx);              \
+            } break;                                                \
+        case 2: {                                                   \
+                ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##2(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 3: {                                                   \
+                ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##3(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 4: {                                                   \
+                ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##4(gAllocOut, i, lx);      \
+            } break;                                                \
+        }                                                           \
+    }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
new file mode 100644
index 0000000..61940ba
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
@@ -0,0 +1,62 @@
+ /*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+#pragma rs_fp_relaxed
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty)                                                 \
+    void __attribute__((kernel)) copy2d_##ty(int xy_v) {            \
+        int lx = xy_v & 0xff;                                       \
+        int vecsize = (xy_v & 0xff0000) >> 16;                      \
+        switch(vecsize) {                                           \
+        case 1: {                                                   \
+                ty i = rsGetElementAt_##ty(gAllocIn, lx);           \
+                rsSetElementAt_##ty(gAllocOut, i, lx);              \
+            } break;                                                \
+        case 2: {                                                   \
+                ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##2(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 3: {                                                   \
+                ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##3(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 4: {                                                   \
+                ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##4(gAllocOut, i, lx);      \
+            } break;                                                \
+        }                                                           \
+    }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
index 9daa2cb..6807523 100644
--- a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
@@ -35,10 +35,10 @@
 {
     aps = 0;
     if (output != NULL) {
-        *output = 0;
+        *output = AUDIO_IO_HANDLE_NONE;
     }
     if (session != NULL) {
-        *session = 0;
+        *session = AUDIO_UNIQUE_ID_ALLOCATE;
     }
 
     int64_t startTime = 0;
@@ -60,19 +60,24 @@
 
     if (output != NULL) {
         // get a valid output. Any use case will do.
-        for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
+        for (int stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT; stream++) {
             *output = AudioSystem::getOutput((audio_stream_type_t)stream);
-            if (*output != 0) {
+            if (*output != AUDIO_IO_HANDLE_NONE) {
                 break;
             }
         }
-        if (*output == 0) {
+        if (*output == AUDIO_IO_HANDLE_NONE) {
             ALOGE("cannot get valid audio output");
             return false;
         }
     }
     if (session != NULL) {
-        *session = 10000;
+        //get a valid session
+        *session = AudioSystem::newAudioUniqueId();
+        if (*session == AUDIO_UNIQUE_ID_ALLOCATE) {
+            ALOGE("cannot get valid audio session");
+            return false;
+        }
     }
     return true;
 }
@@ -92,11 +97,13 @@
         return false;
     }
 
-    status_t status = aps->startOutput(output, (audio_stream_type_t)(-1), session);
+    status_t status = aps->startOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1),
+                                       (audio_session_t)session);
     if (status == NO_ERROR) {
         return false;
     }
-    status = aps->startOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+    status = aps->startOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT,
+                              (audio_session_t)session);
     if (status == NO_ERROR) {
         return false;
     }
@@ -118,11 +125,13 @@
         return false;
     }
 
-    status_t status = aps->stopOutput(output, (audio_stream_type_t)(-1), session);
+    status_t status = aps->stopOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1),
+                                      (audio_session_t)session);
     if (status == NO_ERROR) {
         return false;
     }
-    status = aps->stopOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+    status = aps->stopOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT,
+                             (audio_session_t)session);
     if (status == NO_ERROR) {
         return false;
     }
@@ -153,6 +162,30 @@
     return true;
 }
 
+/*
+ * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+ * invalid stream type.
+ * Test with NUM_RANDOM_TESTS random values for stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_isStreamActiveRemotely(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioPolicyService> aps;
+
+    if (!init(aps, NULL, NULL)) {
+        return false;
+    }
+
+    if (aps->isStreamActiveRemotely((audio_stream_type_t)(AUDIO_STREAM_MIN -1), 0)) {
+        return false;
+    }
+
+    if (aps->isStreamActiveRemotely((audio_stream_type_t)AUDIO_STREAM_CNT, 0)) {
+        return false;
+    }
+    return true;
+}
+
 static JNINativeMethod gMethods[] = {
     {  "native_test_startOutput", "()Z",
             (void *) android_security_cts_AudioPolicy_test_startOutput },
@@ -160,6 +193,8 @@
                 (void *) android_security_cts_AudioPolicy_test_stopOutput },
     {  "native_test_isStreamActive", "()Z",
                 (void *) android_security_cts_AudioPolicy_test_isStreamActive },
+    {  "native_test_isStreamActiveRemotely", "()Z",
+                (void *) android_security_cts_AudioPolicy_test_isStreamActiveRemotely },
 };
 
 int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env)
diff --git a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
index faa6eea..1c45e91 100644
--- a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
@@ -37,6 +37,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <linux/sysctl.h>
+#include <arpa/inet.h>
 
 #define PASSED 0
 #define UNKNOWN_ERROR -1
@@ -282,6 +283,57 @@
     return (ret == -1 && errno == EINVAL);
 }
 
+static jboolean android_security_cts_NativeCodeTest_doNvmapIocFromIdTest(JNIEnv*, jobject)
+{
+    /*
+     * IOCTL code specified from the original notification.
+     * Also available in:
+     *     .../kernel/tegra/drivers/video/tegra/nvmap/nvmap_ioctl.h
+     * #define NVMAP_IOC_MAGIC 'N'
+     * #define NVMAP_IOC_FROM_ID _IOWR(NVMAP_IOC_MAGIC, 2, struct nvmap_create_handle)
+     */
+    const int NVMAP_IOC_FROM_ID = 0xc0084e02;
+    int       nvmap = open("/dev/nvmap", O_RDWR | O_CLOEXEC, 0);
+    bool      vulnerable = false;
+
+    if (nvmap >= 0) {
+        if (0 == ioctl(nvmap, NVMAP_IOC_FROM_ID)) {
+            /* IOCTL succeeded */
+            vulnerable = true;
+        }
+        else if (errno != ENOTTY) {
+            /* IOCTL failed, but provided the wrong error number */
+            vulnerable = true;
+        }
+
+        close(nvmap);
+    }
+
+    return !vulnerable;
+}
+
+static jboolean android_security_cts_NativeCodeTest_doPingPongRootTest(JNIEnv*, jobject)
+{
+    int icmp_sock;
+    struct sockaddr sock_addr;
+
+    memset(&sock_addr, 0, sizeof(sock_addr));
+    icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+    sock_addr.sa_family = AF_INET;
+
+    /* first connect */
+    connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+    /* disconnect */
+    sock_addr.sa_family = AF_UNSPEC;
+    connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+    /* second disconnect -> crash */
+    sock_addr.sa_family = AF_UNSPEC;
+    connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+    return true;
+}
 
 static JNINativeMethod gMethods[] = {
     {  "doPerfEventTest", "()Z",
@@ -296,6 +348,10 @@
             (void *) android_security_cts_NativeCodeTest_doCVE20141710Test },
     {  "doFutexTest", "()Z",
             (void *) android_security_cts_NativeCodeTest_doFutexTest },
+    {  "doNvmapIocFromIdTest", "()Z",
+            (void *) android_security_cts_NativeCodeTest_doNvmapIocFromIdTest },
+    {  "doPingPongRootTest", "()Z",
+            (void *) android_security_cts_NativeCodeTest_doPingPongRootTest },
 };
 
 int register_android_security_cts_NativeCodeTest(JNIEnv* env)
diff --git a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
index 399d8bb..b307247 100644
--- a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
+++ b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
@@ -48,7 +48,16 @@
         assertTrue(native_test_isStreamActive());
     }
 
+    /**
+     * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+     * invalid stream type.
+     */
+    public void test_isStreamActiveRemotely() throws Exception {
+        assertTrue(native_test_isStreamActiveRemotely());
+    }
+
     private static native boolean native_test_startOutput();
     private static native boolean native_test_stopOutput();
     private static native boolean native_test_isStreamActive();
+    private static native boolean native_test_isStreamActiveRemotely();
 }
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index da098f3..de7c15e 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -28,9 +28,11 @@
       "91:C6:D6:EE:3E:8A:C8:63:84:E5:48:C2:99:29:5C:75:6C:81:7B:81",
       "4A:65:D5:F4:1D:EF:39:B8:B8:90:4A:4A:D3:64:81:33:CF:C7:A1:D1",
       "16:32:47:8D:89:F9:21:3A:92:00:85:63:F5:A4:A7:D3:12:40:8A:D6",
+      "D1:CB:CA:5D:B2:D5:2A:7F:69:3B:67:4D:E5:F0:5A:1D:0C:95:7D:F0",
       "4D:23:78:EC:91:95:39:B5:00:7F:75:8F:03:3B:21:1E:C5:4D:8B:CF",
       "E7:B4:F6:9D:61:EC:90:69:DB:7E:90:A7:40:1A:3C:F4:7D:4F:E8:EE",
       "DD:E1:D2:A9:01:80:2E:1D:87:5E:84:B3:80:7E:4B:B1:FD:99:41:34",
+      "69:69:56:2E:40:80:F4:24:A1:E7:19:9F:14:BA:F3:EE:58:AB:6A:BB",
       "92:5A:8F:8D:2C:6D:04:E0:66:5F:59:6A:FF:22:D8:63:E8:25:6F:3F",
       "75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE",
       "40:9D:4B:D9:17:B5:5C:27:B6:9B:64:CB:98:22:44:0D:CD:09:B8:89",
@@ -49,6 +51,7 @@
       "27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4",
       "AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
       "8D:17:84:D5:37:F3:03:7D:EC:70:FE:57:8B:51:9A:99:E6:10:D7:B0",
+      "1F:24:C6:30:CD:A4:18:EF:20:69:FF:AD:4F:DD:5F:46:3A:1B:69:AA",
       "AE:50:83:ED:7C:F4:5C:BC:8F:61:C6:21:FE:68:5D:79:42:21:15:6E",
       "DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57",
       "74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
@@ -56,6 +59,7 @@
       "3E:2B:F7:F2:03:1B:96:F3:8C:E6:C4:D8:A8:5D:3E:2D:58:47:6A:0F",
       "A3:F1:33:3F:E2:42:BF:CF:C5:D1:4E:8F:39:42:98:40:68:10:D1:A0",
       "5F:43:E5:B1:BF:F8:78:8C:AC:1C:C7:CA:4A:9A:C6:22:2B:CC:34:C6",
+      "2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E",
       "A8:98:5D:3A:65:E5:E5:C4:B2:D7:D6:6D:40:C6:DD:2F:B1:9C:54:36",
       "59:22:A1:E1:5A:EA:16:35:21:F8:98:39:6A:46:46:B0:44:1B:0F:A9",
       "D4:DE:20:D0:5E:66:FC:53:FE:1A:50:88:2C:78:DB:28:52:CA:E4:74",
@@ -155,6 +159,7 @@
       "87:82:C6:C3:04:35:3B:CF:D2:96:92:D2:59:3E:7D:44:D9:34:FF:11",
       "59:0D:2D:7D:88:4F:40:2E:61:7E:A5:62:32:17:65:CF:17:D8:94:E9",
       "AE:C5:FB:3F:C8:E1:BF:C4:E5:4F:03:07:5A:9A:E8:00:B7:F7:B6:FA",
+      "AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4",
       "5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
       "2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41",
       "F1:7F:6F:B6:31:DC:99:E3:A3:C8:7F:FE:1C:F1:81:10:88:D9:60:33",
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
index 8ebe6ac..dfefad7 100644
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
@@ -120,6 +120,7 @@
          */
         int firstPid = -1;
         int previousPid = -1;
+        int lastPid = -1;
         for (int i = 0; i < MAX_PID; i++) {
             byte[] output = new byte[RANDOM_BYTES_PER_PID];
             int pid;
@@ -202,7 +203,9 @@
                 firstPid = pid;
             }
 
-            if (i > PRIMING_ITERATIONS) {
+            if (i <= PRIMING_ITERATIONS) {
+                lastPid = pid;
+            } else if (pid > lastPid && (lastPid > firstPid || pid < firstPid)) {
                 wastePids(firstPid, previousPid);
             }
         }
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index 4be00b6..5ee8f69 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -56,6 +56,21 @@
                    doFutexTest());
     }
 
+    public void testNvmapIocFromId() throws Exception {
+        assertTrue("Device is vulnerable to CVE-2014-5332. "
+                   + "NVIDIA has released code fixes to upstream repositories and device vendors. "
+                   + "For more information, see "
+                   + "https://nvidia.custhelp.com/app/answers/detail/a_id/3618",
+                   doNvmapIocFromIdTest());
+    }
+
+    public void testPingPongRoot() throws Exception {
+        assertTrue("Device is vulnerable to CVE-2015-3636, a vulnerability in the ping "
+                   + "socket implementation. Please apply the security patch at "
+                   + "https://github.com/torvalds/linux/commit/a134f083e79f",
+                   doPingPongRootTest());
+    }
+
     /**
      * Returns true iff this device is vulnerable to CVE-2013-2094.
      * A patch for CVE-2013-2094 can be found at
@@ -110,8 +125,31 @@
     private static native boolean doFutexTest();
 
     /**
+     * ANDROID-17453812 / CVE-2014-5332
+     *
+     * Returns true if the device is patched against the NVMAP_IOC_FROM_ID ioctl call.
+     *
+     * More information on this vulnreability is available at
+     * https://nvidia.custhelp.com/app/answers/detail/a_id/3618
+     */
+    private static native boolean doNvmapIocFromIdTest();
+
+    /**
      * Returns true if the device is immune to CVE-2014-1710,
      * false if the device is vulnerable.
      */
     private static native boolean doCVE20141710Test();
+
+    /**
+     * CVE-2015-3636
+     *
+     * Returns true if the patch is applied, crashes the system otherwise.
+     *
+     * Detects if the following patch is present.
+     * https://github.com/torvalds/linux/commit/a134f083e79f
+     *
+     * Credit: Wen Xu and wushi of KeenTeam.
+     * http://seclists.org/oss-sec/2015/q2/333
+     */
+    private static native boolean doPingPongRootTest();
 }
diff --git a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
index 66054f9..c97c8c2 100644
--- a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
+++ b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
@@ -265,15 +265,15 @@
     }
 
     /*
-     * Their will at least be some kernel thread running and all kthreads should
-     * be in kernel context.
+     * All kthreads should be in kernel context.
      */
     public void testKernelDomain() throws FileNotFoundException {
         String domain = "u:r:kernel:s0";
         List<ProcessDetails> procs = ProcessDetails.getProcessMap().get(domain);
-        assertNotNull(procs);
-        for (ProcessDetails p : procs) {
-            assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel());
+        if (procs != null) {
+            for (ProcessDetails p : procs) {
+                assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel());
+            }
         }
     }
 
@@ -354,23 +354,47 @@
         }
 
         private static ProcessDetails getProcessDetails(int pid, File f) throws FileNotFoundException {
-            // Get the context via attr/current
-            String context = new Scanner(new File(f, "attr/current")).next();
-            context = context.trim();
+            Scanner tmp = null;
+            String context;
+            long vSize;
+            try {
+                tmp = new Scanner(new File(f, "attr/current"));
+                // Get the context via attr/current
+                context = tmp.next();
+                context = context.trim();
+            } finally {
+                if (tmp != null) {
+                    tmp.close();
+                    tmp = null;
+                }
+            }
 
-            // Get the vSize, item #23 from the stat file
-            String x = new Scanner(new File(f, "stat")).nextLine();
-            long vSize = getVsizeFromStat(x);
+            try {
+                // Get the vSize, item #23 from the stat file
+                tmp = new Scanner(new File(f, "stat"));
+                String x = tmp.nextLine();
+                vSize = getVsizeFromStat(x);
+            } finally {
+                if (tmp != null) {
+                    tmp.close();
+                    tmp = null;
+                }
+            }
 
             StringBuilder sb = new StringBuilder();
-            Scanner tmp = new Scanner(new File(f, "cmdline"));
+            try {
+                tmp = new Scanner(new File(f, "cmdline"));
 
-            // Java's scanner tends to return oddly when handling
-            // long binary blobs. Probably some caching optimization.
-            while (tmp.hasNext()) {
-                sb.append(tmp.next().replace('\0', ' '));
+                // Java's scanner tends to return oddly when handling
+                // long binary blobs. Probably some caching optimization.
+                while (tmp.hasNext()) {
+                    sb.append(tmp.next().replace('\0', ' '));
+                }
+            } finally {
+                if (tmp != null) {
+                    tmp.close();
+                }
             }
-            tmp.close();
 
             // At this point we build up a valid proctitle, then split
             // on whitespace to get the left portion. Which is either
diff --git a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
new file mode 100644
index 0000000..cb9d96c
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.telephony.cts;
+
+import android.content.Context;
+import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+
+public class SimRestrictedApisTest extends AndroidTestCase {
+    private static final byte[] TEST_PDU = {
+            0, 0 };
+    private TelephonyManager mTelephonyManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTelephonyManager =
+                (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
+    }
+
+    private boolean isSimCardPresent() {
+        return mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE &&
+                mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT;
+    }
+
+    /**
+     * Tests the SmsManager.injectSmsPdu() API. This makes a call to injectSmsPdu() API and expects
+     * a SecurityException since the test apk is not signed by a certificate on the SIM.
+     */
+    public void testInjectSmsPdu() {
+        try {
+            if (isSimCardPresent()) {
+                SmsManager.getDefault().injectSmsPdu(TEST_PDU, "3gpp", null);
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.setLine1NumberForDisplay() API. This makes a call to
+     * setLine1NumberForDisplay() API and expects a SecurityException since the test apk is not
+     * signed by a certificate on the SIM.
+     */
+    public void testSetLine1NumberForDisplay() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().setLine1NumberForDisplay("", "");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.setLine1NumberForDisplay(long, string, string) API. This makes a
+     * call to setLine1NumberForDisplay() API and expects a SecurityException since the test apk is
+     * not signed by the certificate on the SIM.
+     */
+    public void testSetLine1NumberForDisplay2() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().setLine1NumberForDisplayForSubscriber(0, "", "");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.iccOpenLogicalChannel() API. This makes a call to
+     * iccOpenLogicalChannel() API and expects a SecurityException since the test apk is not signed
+     * by certificate on the SIM.
+     */
+    public void testIccOpenLogicalChannel() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().iccOpenLogicalChannel("");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.iccCloseLogicalChannel() API. This makes a call to
+     * iccCloseLogicalChannel() API and expects a SecurityException since the test apk is not signed
+     * by certificate on the SIM.
+     */
+    public void testIccCloseLogicalChannel() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().iccCloseLogicalChannel(0);
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.iccTransmitApduLogicalChannel() API. This makes a call to
+     * iccTransmitApduLogicalChannel() API and expects a SecurityException since the test apk is not
+     * signed by a certificate on the SIM.
+     */
+    public void testIccTransmitApduLogicalChannel() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().iccTransmitApduLogicalChannel(0, 0, 0, 0, 0, 0, "");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.iccTransmitApduBasicChannel() API. This makes a call to
+     * iccTransmitApduBasicChannel() API and expects a SecurityException since the test apk is not
+     * signed by a certificate on the SIM.
+     */
+    public void testIccTransmitApduBasicChannel() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().iccTransmitApduBasicChannel(0, 0, 0, 0, 0, "");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.sendEnvelopeWithStatus() API. This makes a call to
+     * sendEnvelopeWithStatus() API and expects a SecurityException since the test apk is not signed
+     * by certificate on the SIM.
+     */
+    public void testSendEnvelopeWithStatus() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().sendEnvelopeWithStatus("");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.nvReadItem() API. This makes a call to nvReadItem() API and
+     * expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+     */
+    public void testNvReadItem() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().nvReadItem(0);
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.nvWriteItem() API. This makes a call to nvWriteItem() API and
+     * expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+     */
+    public void testNvWriteItem() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().nvWriteItem(0, "");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.nvWriteCdmaPrl() API. This makes a call to nvWriteCdmaPrl() API
+     * and expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+     */
+    public void testNvWriteCdmaPrl() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().nvWriteCdmaPrl(null);
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.nvResetConfig() API. This makes a call to nvResetConfig() API and
+     * expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+     */
+    public void testNvResetConfig() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().nvResetConfig(0);
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.getPreferredNetworkType() API. This makes a call to
+     * getPreferredNetworkType() API and expects a SecurityException since the test apk is not
+     * signed by certificate on the SIM.
+     */
+    public void testGetPreferredNetworkType() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().getPreferredNetworkType();
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.setPreferredNetworkTypeToGlobal() API. This makes a call to
+     * setPreferredNetworkTypeToGlobal() API and expects a SecurityException since the test apk is not
+     * signed by certificate on the SIM.
+     */
+    public void testSetPreferredNetworkTypeToGlobal() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().setPreferredNetworkTypeToGlobal();
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests that the test apk doesn't have carrier previliges.
+     */
+    public void testHasCarrierPrivileges() {
+        if (TelephonyManager.getDefault().hasCarrierPrivileges()) {
+            fail("App unexpectedly has carrier privileges");
+        }
+    }
+
+    /**
+     * Tests the TelephonyManager.setOperatorBrandOverride() API. This makes a call to
+     * setOperatorBrandOverride() API and expects a SecurityException since the test apk is not
+     * signed by certificate on the SIM.
+     */
+    public void testSetOperatorBrandOverride() {
+        try {
+            if (isSimCardPresent()) {
+                TelephonyManager.getDefault().setOperatorBrandOverride("");
+                fail("Expected SecurityException. App doesn't have carrier privileges.");
+            }
+        } catch (SecurityException expected) {
+        }
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
old mode 100644
new mode 100755
index 39f5177..92f352f
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -66,6 +66,7 @@
                     "311660",   // MetroPCS
                     "310120",   // Sprint
                     "44050",    // KDDI
+                    "44051",    // KDDI
                     "44053",    // KDDI
                     "44054",    // KDDI
                     "44070",    // KDDI
@@ -79,6 +80,8 @@
                     "51502",    // Globe Telecoms
                     "51503",    // Smart Communications
                     "51505",    // Sun Cellular
+                    "53001",    // Vodafone New Zealand
+                    "53024",    // NZC
                     "311870",   // Boost Mobile
                     "311220",   // USCC
                     "311225",   // USCC LTE
@@ -95,6 +98,7 @@
                     "310600",    // Cellcom
                     "31000",     // Republic Wireless US
                     "310026",     // T-Mobile US
+                    "330120", // OpenMobile communication
                     // Verizon
                     "310004",
                     "310012",
@@ -125,6 +129,7 @@
             Arrays.asList(
                     "44010",    // NTT DOCOMO
                     "44020",    // SBM
+                    "44051",    // KDDI
                     "302720",   // Rogers
                     "30272",    // Rogers
                     "302370",   // Fido
@@ -163,6 +168,7 @@
             Arrays.asList(
                     "44010",    // NTT DOCOMO
                     "44020",    // SBM
+                    "44051",    // KDDI
                     "302720",   // Rogers
                     "30272",    // Rogers
                     "302370",   // Fido
diff --git a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
new file mode 100644
index 0000000..9b08397
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import android.content.Context;
+import android.cts.util.TestThread;
+import android.net.ConnectivityManager;
+import android.os.Looper;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.List;
+
+public class SubscriptionManagerTest extends AndroidTestCase {
+    private SubscriptionManager mSubscriptionManager;
+    private static ConnectivityManager mCm;
+    private SubscriptionManager.OnSubscriptionsChangedListener mListener;
+    private final Object mLock = new Object();
+    private boolean mOnSubscriptionsChangedCalled = false;
+    private static final int TOLERANCE = 1000;
+    private static final String TAG = "android.telephony.cts.SubscriptionManagerTest";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSubscriptionManager = new SubscriptionManager(getContext());
+        mCm = (ConnectivityManager)getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mListener != null) {
+            // unregister the listener
+            mSubscriptionManager.removeOnSubscriptionsChangedListener(mListener);
+        }
+        super.tearDown();
+    }
+
+    public void testGetActiveSubscriptionInfoCount() {
+        if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+            Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+            return;
+        }
+        assertTrue(mSubscriptionManager.getActiveSubscriptionInfoCount() <=
+                mSubscriptionManager.getActiveSubscriptionInfoCountMax());
+    }
+
+    public void testActiveSubscriptions() {
+        if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+            Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+            return;
+        }
+        List<SubscriptionInfo> subList = mSubscriptionManager.getActiveSubscriptionInfoList();
+        // Assert when there is no sim card present or detected
+        assertTrue(subList != null && subList.size() > 0);
+        for (int i = 0; i < subList.size(); i++) {
+            assertTrue(subList.get(i).getSubscriptionId() >= 0);
+            assertTrue(subList.get(i).getSimSlotIndex() >= 0);
+        }
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 8575c32..2be1dcb 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -81,9 +81,11 @@
                 mListener = new PhoneStateListener() {
                     @Override
                     public void onCellLocationChanged(CellLocation location) {
-                        synchronized (mLock) {
-                            mOnCellLocationChangedCalled = true;
-                            mLock.notify();
+                        if(!mOnCellLocationChangedCalled) {
+                            synchronized (mLock) {
+                                mOnCellLocationChangedCalled = true;
+                                mLock.notify();
+                            }
                         }
                     }
                 };
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index dc5d30a..d2b3ddf 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -75,6 +75,16 @@
                        android:resource="@xml/stub_tv_input_service" />
         </service>
 
+        <service android:name="android.media.tv.cts.HardwareSessionTest$HardwareProxyTvInputService"
+                 android:permission="android.permission.BIND_TV_INPUT">
+            <intent-filter>
+                <action android:name="android.media.tv.TvInputService" />
+            </intent-filter>
+            <meta-data android:name="android.media.tv.input"
+                       android:resource="@xml/stub_tv_input_service" />
+        </service>
+
+
         <activity android:name="android.media.tv.cts.TvViewStubActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
new file mode 100644
index 0000000..75cf28d
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.media.tv.cts;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.cts.util.PollingCheck;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.TvView;
+import android.media.tv.cts.HardwareSessionTest.HardwareProxyTvInputService.CountingSession;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.android.cts.tv.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test {@link android.media.tv.TvInputService.HardwareSession}.
+ */
+public class HardwareSessionTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
+    /** The maximum time to wait for an operation. */
+    private static final long TIME_OUT = 15000L;
+
+    private TvView mTvView;
+    private Activity mActivity;
+    private Instrumentation mInstrumentation;
+    private TvInputManager mManager;
+    private TvInputInfo mStubInfo;
+    private final List<TvInputInfo> mPassthroughInputList = new ArrayList<>();
+
+    public HardwareSessionTest() {
+        super(TvViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        mActivity = getActivity();
+        mInstrumentation = getInstrumentation();
+        mTvView = (TvView) mActivity.findViewById(R.id.tvview);
+        mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
+        for (TvInputInfo info : mManager.getTvInputList()) {
+            if (info.getServiceInfo().name.equals(HardwareProxyTvInputService.class.getName())) {
+                mStubInfo = info;
+            }
+            if (info.isPassthroughInput()) {
+                mPassthroughInputList.add(info);
+            }
+        }
+        assertNotNull(mStubInfo);
+    }
+
+    public void testHardwareProxyTvInputService() throws Throwable {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        for (final TvInputInfo info : mPassthroughInputList) {
+            verifyCommandTuneAndHardwareVideoAvailable(info);
+        }
+    }
+
+    public void verifyCommandTuneAndHardwareVideoAvailable(TvInputInfo passthroughInfo) throws
+            Throwable {
+        HardwareProxyTvInputService.sHardwareInputId = passthroughInfo.getId();
+        Uri fakeChannelUri = TvContract.buildChannelUri(0);
+        mTvView.tune(mStubInfo.getId(), fakeChannelUri);
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = HardwareProxyTvInputService.sSession;
+                return session != null && session.mTuneCount > 0
+                        && session.mHardwareVideoAvailableCount > 0;
+            }
+        }.run();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mTvView.reset();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        HardwareProxyTvInputService.sSession = null;
+    }
+
+    public static class HardwareProxyTvInputService extends TvInputService {
+        static String sHardwareInputId;
+        static CountingSession sSession;
+
+        @Override
+        public Session onCreateSession(String inputId) {
+            sSession = new CountingSession(this);
+            return sSession;
+        }
+
+        public static class CountingSession extends HardwareSession {
+            public volatile int mTuneCount;
+            public volatile int mHardwareVideoAvailableCount;
+
+            CountingSession(Context context) {
+                super(context);
+            }
+
+            @Override
+            public void onRelease() {
+            }
+
+            @Override
+            public void onSetCaptionEnabled(boolean enabled) {
+            }
+
+            @Override
+            public String getHardwareInputId() {
+                return sHardwareInputId;
+            }
+
+            @Override
+            public void onSetStreamVolume(float volume) {
+            }
+
+            @Override
+            public boolean onTune(Uri channelUri) {
+                mTuneCount++;
+                return true;
+            }
+
+            @Override
+            public void onHardwareVideoAvailable() {
+                mHardwareVideoAvailableCount++;
+            }
+        }
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index 651a199..6a50b56 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -26,6 +26,8 @@
 import android.graphics.BitmapFactory;
 import android.media.tv.TvContentRating;
 import android.media.tv.TvContract;
+import android.media.tv.TvContract.Channels;
+import android.media.tv.TvContract.Programs.Genres;
 import android.net.Uri;
 import android.test.AndroidTestCase;
 
@@ -33,6 +35,8 @@
 
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Test for {@link android.media.tv.TvContract}.
@@ -80,6 +84,11 @@
 
     private static long OPERATION_TIME = 1000l;
 
+    private static final String ENCODED_GENRE_STRING = Genres.ANIMAL_WILDLIFE + "," + Genres.COMEDY
+            + "," + Genres.DRAMA + "," + Genres.EDUCATION + "," + Genres.FAMILY_KIDS + ","
+            + Genres.GAMING + "," + Genres.MOVIES + "," + Genres.NEWS + "," + Genres.SHOPPING + ","
+            + Genres.SPORTS + "," + Genres.TRAVEL;
+
     private String mInputId;
     private ContentResolver mContentResolver;
     private Uri mChannelsUri;
@@ -395,4 +404,185 @@
         verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0,
                 channelId, channelUri);
     }
+
+    private void verifyQueryWithSortOrder(Uri uri, final String[] projection,
+            String sortOrder) throws Exception {
+        try {
+            getContext().getContentResolver().query(uri, projection, null, null, sortOrder);
+            fail("Setting sortOrder should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    private void verifyQueryWithSelection(Uri uri, final String[] projection,
+            String selection) throws Exception {
+        try {
+            getContext().getContentResolver().query(uri, projection, selection, null, null);
+            fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    private void verifyUpdateWithSelection(Uri uri, String selection) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().update(uri, values, selection, null);
+            fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    private void verifyDeleteWithSelection(Uri uri, String selection) throws Exception {
+        try {
+            getContext().getContentResolver().delete(uri, selection, null);
+            fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void testAllEpgPermissionBlocksSortOrderOnQuery_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQueryWithSortOrder(TvContract.Channels.CONTENT_URI, projection,
+                TvContract.Channels._ID + " ASC");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnQuery_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQueryWithSelection(TvContract.Channels.CONTENT_URI, projection,
+                TvContract.Channels._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnUpdate_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyUpdateWithSelection(TvContract.Channels.CONTENT_URI,
+                TvContract.Channels._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnDelete_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyDeleteWithSelection(TvContract.Channels.CONTENT_URI,
+                TvContract.Channels._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSortOrderOnQuery_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Programs._ID };
+        verifyQueryWithSortOrder(TvContract.Programs.CONTENT_URI, projection,
+                TvContract.Programs._ID + " ASC");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnQuery_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQueryWithSelection(TvContract.Programs.CONTENT_URI, projection,
+                TvContract.Programs._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnUpdate_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyUpdateWithSelection(TvContract.Programs.CONTENT_URI,
+                TvContract.Programs._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnDelete_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyDeleteWithSelection(TvContract.Programs.CONTENT_URI,
+                TvContract.Programs._ID + ">0");
+    }
+
+    public void testDefaultValues() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        ContentValues values = new ContentValues();
+        values.put(TvContract.Channels.COLUMN_INPUT_ID, mInputId);
+        Uri channelUri = mContentResolver.insert(mChannelsUri, values);
+        assertNotNull(channelUri);
+        try (Cursor cursor = mContentResolver.query(
+                channelUri, CHANNELS_PROJECTION, null, null, null)) {
+            cursor.moveToNext();
+            assertEquals(TvContract.Channels.TYPE_OTHER,
+                    cursor.getString(cursor.getColumnIndex(TvContract.Channels.COLUMN_TYPE)));
+            assertEquals(TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO,
+                    cursor.getString(cursor.getColumnIndex(
+                            TvContract.Channels.COLUMN_SERVICE_TYPE)));
+        }
+        values.clear();
+    }
+
+    public void testChannelsGetVideoResolution() {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_480I));
+        assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_480P));
+        assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_576I));
+        assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_576P));
+        assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_720P));
+        assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_1080I));
+        assertEquals(Channels.VIDEO_RESOLUTION_FHD, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_1080P));
+        assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_2160P));
+        assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
+                Channels.VIDEO_FORMAT_4320P));
+        assertEquals(null, Channels.getVideoResolution("Unknown format"));
+    }
+
+    public void testProgramsGenresDecode() {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        List genres = Arrays.asList(Genres.decode(ENCODED_GENRE_STRING));
+        assertEquals(11, genres.size());
+        assertTrue(genres.contains(Genres.ANIMAL_WILDLIFE));
+        assertTrue(genres.contains(Genres.COMEDY));
+        assertTrue(genres.contains(Genres.DRAMA));
+        assertTrue(genres.contains(Genres.EDUCATION));
+        assertTrue(genres.contains(Genres.FAMILY_KIDS));
+        assertTrue(genres.contains(Genres.GAMING));
+        assertTrue(genres.contains(Genres.MOVIES));
+        assertTrue(genres.contains(Genres.NEWS));
+        assertTrue(genres.contains(Genres.SHOPPING));
+        assertTrue(genres.contains(Genres.SPORTS));
+        assertTrue(genres.contains(Genres.TRAVEL));
+        assertFalse(genres.contains(","));
+    }
+
+    public void testProgramsGenresEncode() {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertEquals(ENCODED_GENRE_STRING, Genres.encode(Genres.ANIMAL_WILDLIFE,
+                Genres.COMEDY, Genres.DRAMA, Genres.EDUCATION, Genres.FAMILY_KIDS, Genres.GAMING,
+                Genres.MOVIES, Genres.NEWS, Genres.SHOPPING, Genres.SPORTS, Genres.TRAVEL));
+    }
 }
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
index 440ecb2..de91916 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
@@ -20,8 +20,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvInputManager;
+import android.os.Parcel;
 import android.test.AndroidTestCase;
 
 /**
@@ -48,6 +50,53 @@
         mPackageManager = getContext().getPackageManager();
     }
 
+    public void testTvInputInfoOp() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        // Test describeContents
+        assertEquals(0, mStubInfo.describeContents());
+
+        // Test equals
+        assertTrue(mStubInfo.equals(mStubInfo));
+
+        // Test getId
+        final ComponentName componentName =
+                new ComponentName(getContext(), StubTunerTvInputService.class);
+        final String id = TvContract.buildInputId(componentName);
+        assertEquals(id, mStubInfo.getId());
+
+        // Test getServiceInfo
+        assertEquals(getContext().getPackageManager().getServiceInfo(componentName, 0).name,
+                mStubInfo.getServiceInfo().name);
+
+        // Test hashCode
+        assertEquals(id.hashCode(), mStubInfo.hashCode());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        mStubInfo.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        TvInputInfo infoFromParcel = TvInputInfo.CREATOR.createFromParcel(p);
+        assertEquals(mStubInfo.createSettingsIntent().getComponent(),
+                infoFromParcel.createSettingsIntent().getComponent());
+        assertEquals(mStubInfo.createSetupIntent().getComponent(),
+                infoFromParcel.createSetupIntent().getComponent());
+        assertEquals(mStubInfo.describeContents(), infoFromParcel.describeContents());
+        assertTrue(mStubInfo.equals(infoFromParcel));
+        assertEquals(mStubInfo.getId(), infoFromParcel.getId());
+        assertEquals(mStubInfo.getParentId(), infoFromParcel.getParentId());
+        assertEquals(mStubInfo.getServiceInfo().name, infoFromParcel.getServiceInfo().name);
+        assertEquals(mStubInfo.getType(), infoFromParcel.getType());
+        assertEquals(mStubInfo.hashCode(), infoFromParcel.hashCode());
+        assertEquals(mStubInfo.isPassthroughInput(), infoFromParcel.isPassthroughInput());
+        assertEquals(mStubInfo.loadIcon(getContext()).getConstantState(),
+                infoFromParcel.loadIcon(getContext()).getConstantState());
+        assertEquals(mStubInfo.loadLabel(getContext()), infoFromParcel.loadLabel(getContext()));
+        assertEquals(mStubInfo.toString(), infoFromParcel.toString());
+        p.recycle();
+    }
+
     public void testGetIntentForSettingsActivity() throws Exception {
         if (!Utils.hasTvInputFramework(getContext())) {
             return;
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
index 790adf9..48f1f44 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
@@ -17,25 +17,30 @@
 package android.media.tv.cts;
 
 import android.content.Context;
+import android.media.tv.TvContentRating;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvInputManager;
-import android.test.AndroidTestCase;
+import android.os.Handler;
+import android.test.ActivityInstrumentationTestCase2;
 
 import java.util.List;
 
 /**
  * Test for {@link android.media.tv.TvInputManager}.
  */
-public class TvInputManagerTest extends AndroidTestCase {
+public class TvInputManagerTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
     private static final String[] VALID_TV_INPUT_SERVICES = {
         StubTunerTvInputService.class.getName()
     };
     private static final String[] INVALID_TV_INPUT_SERVICES = {
         NoMetadataTvInputService.class.getName(), NoPermissionTvInputService.class.getName()
     };
+    private static final TvContentRating DUMMY_RATING = TvContentRating.createRating(
+            "com.android.tv", "US_TV", "US_TV_PG", "US_TV_D", "US_TV_L");
 
     private String mStubId;
     private TvInputManager mManager;
+    private TvInputManager.TvInputCallback mCallabck = new TvInputManager.TvInputCallback() {};
 
     private static TvInputInfo getInfoForClassName(List<TvInputInfo> list, String name) {
         for (TvInputInfo info : list) {
@@ -46,25 +51,29 @@
         return null;
     }
 
+    public TvInputManagerTest() {
+        super(TvViewStubActivity.class);
+    }
+
     @Override
     public void setUp() throws Exception {
-        if (!Utils.hasTvInputFramework(getContext())) {
+        if (!Utils.hasTvInputFramework(getActivity())) {
             return;
         }
-        mManager = (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE);
+        mManager = (TvInputManager) getActivity().getSystemService(Context.TV_INPUT_SERVICE);
         mStubId = getInfoForClassName(
                 mManager.getTvInputList(), StubTunerTvInputService.class.getName()).getId();
     }
 
     public void testGetInputState() throws Exception {
-        if (!Utils.hasTvInputFramework(getContext())) {
+        if (!Utils.hasTvInputFramework(getActivity())) {
             return;
         }
         assertEquals(mManager.getInputState(mStubId), TvInputManager.INPUT_STATE_CONNECTED);
     }
 
     public void testGetTvInputInfo() throws Exception {
-        if (!Utils.hasTvInputFramework(getContext())) {
+        if (!Utils.hasTvInputFramework(getActivity())) {
             return;
         }
         assertEquals(mManager.getTvInputInfo(mStubId), getInfoForClassName(
@@ -72,7 +81,7 @@
     }
 
     public void testGetTvInputList() throws Exception {
-        if (!Utils.hasTvInputFramework(getContext())) {
+        if (!Utils.hasTvInputFramework(getActivity())) {
             return;
         }
         List<TvInputInfo> list = mManager.getTvInputList();
@@ -85,4 +94,44 @@
                     getInfoForClassName(list, name));
         }
     }
+
+    public void testIsParentalControlsEnabled() {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        try {
+            mManager.isParentalControlsEnabled();
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+    public void testIsRatingBlocked() {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        try {
+            mManager.isRatingBlocked(DUMMY_RATING);
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+    public void testRegisterUnregisterCallback() {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    mManager.registerCallback(mCallabck, new Handler());
+                    mManager.unregisterCallback(mCallabck);
+                } catch (Exception e) {
+                    fail();
+                }
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+    }
 }
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
index f0ee277..7068035 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
@@ -28,9 +28,13 @@
 import android.media.tv.TvView;
 import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingSession;
 import android.net.Uri;
+import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
+import android.view.InputDevice;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.Surface;
+import android.view.View;
 
 import com.android.cts.tv.R;
 
@@ -46,8 +50,8 @@
     private static final long TIME_OUT = 15000L;
     private static final String mDummyTrackId = "dummyTrackId";
     private static final TvTrackInfo mDummyTrack =
-            new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, mDummyTrackId)
-            .setLanguage("und").build();
+            new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, mDummyTrackId)
+            .setVideoWidth(1920).setVideoHeight(1080).setLanguage("und").build();
 
     private TvView mTvView;
     private Activity mActivity;
@@ -62,6 +66,7 @@
         private int mVideoUnavailableCount;
         private int mTrackSelectedCount;
         private int mTrackChangedCount;
+        private int mVideoSizeChanged;
         private int mContentAllowedCount;
         private int mContentBlockedCount;
 
@@ -91,6 +96,11 @@
         }
 
         @Override
+        public void onVideoSizeChanged(String inputId, int width, int height) {
+            mVideoSizeChanged++;
+        }
+
+        @Override
         public void onContentAllowed(String inputId) {
             mContentAllowedCount++;
         }
@@ -99,6 +109,16 @@
         public void onContentBlocked(String inputId, TvContentRating rating) {
             mContentBlockedCount++;
         }
+
+        public void resetCounts() {
+            mChannelRetunedCount = 0;
+            mVideoAvailableCount = 0;
+            mVideoUnavailableCount = 0;
+            mTrackSelectedCount = 0;
+            mTrackChangedCount = 0;
+            mContentAllowedCount = 0;
+            mContentBlockedCount = 0;
+        }
     }
 
     public TvInputServiceTest() {
@@ -135,7 +155,12 @@
         verifyCommandSetStreamVolume();
         verifyCommandSetCaptionEnabled();
         verifyCommandSelectTrack();
-        verifyCommandDispatchKeyEvent();
+        verifyCommandDispatchKeyDown();
+        verifyCommandDispatchKeyMultiple();
+        verifyCommandDispatchKeyUp();
+        verifyCommandDispatchTouchEvent();
+        verifyCommandDispatchTrackballEvent();
+        verifyCommandDispatchGenericMotionEvent();
         verifyCallbackChannelRetuned();
         verifyCallbackVideoAvailable();
         verifyCallbackVideoUnavailable();
@@ -161,12 +186,13 @@
             @Override
             protected boolean check() {
                 CountingSession session = CountingTvInputService.sSession;
-                return session != null && session.mTuneCount > 0;
+                return session != null && session.mTuneCount > 0 && session.mCreateOverlayView > 0;
             }
         }.run();
     }
 
     public void verifyCommandSetStreamVolume() {
+        resetCounts();
         mTvView.setStreamVolume(1.0f);
         mInstrumentation.waitForIdleSync();
         new PollingCheck(TIME_OUT) {
@@ -179,6 +205,7 @@
     }
 
     public void verifyCommandSetCaptionEnabled() {
+        resetCounts();
         mTvView.setCaptionEnabled(true);
         mInstrumentation.waitForIdleSync();
         new PollingCheck(TIME_OUT) {
@@ -191,18 +218,21 @@
     }
 
     public void verifyCommandSelectTrack() {
-        mTvView.selectTrack(TvTrackInfo.TYPE_AUDIO, "dummyTrackId");
+        resetCounts();
+        verifyCallbackTracksChanged();
+        mTvView.selectTrack(mDummyTrack.getType(), mDummyTrack.getId());
         mInstrumentation.waitForIdleSync();
         new PollingCheck(TIME_OUT) {
             @Override
             protected boolean check() {
                 CountingSession session = CountingTvInputService.sSession;
-                return session != null && session.mSetStreamVolumeCount > 0;
+                return session != null && session.mSelectTrackCount > 0;
             }
         }.run();
     }
 
-    public void verifyCommandDispatchKeyEvent() {
+    public void verifyCommandDispatchKeyDown() {
+        resetCounts();
         mTvView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_K));
         mInstrumentation.waitForIdleSync();
         new PollingCheck(TIME_OUT) {
@@ -214,7 +244,84 @@
         }.run();
     }
 
+    public void verifyCommandDispatchKeyMultiple() {
+        resetCounts();
+        mTvView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_K));
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mKeyMultipleCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandDispatchKeyUp() {
+        resetCounts();
+        mTvView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_K));
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mKeyUpCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandDispatchTouchEvent() {
+        resetCounts();
+        long now = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
+                1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
+        event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        mTvView.dispatchTouchEvent(event);
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mTouchEventCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandDispatchTrackballEvent() {
+        resetCounts();
+        long now = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
+                1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
+        event.setSource(InputDevice.SOURCE_TRACKBALL);
+        mTvView.dispatchTouchEvent(event);
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mTrackballEventCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandDispatchGenericMotionEvent() {
+        resetCounts();
+        long now = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
+                1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
+        mTvView.dispatchGenericMotionEvent(event);
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mGenricMotionEventCount > 0;
+            }
+        }.run();
+    }
+
     public void verifyCallbackChannelRetuned() {
+        resetCounts();
         CountingSession session = CountingTvInputService.sSession;
         assertNotNull(session);
         Uri fakeChannelUri = TvContract.buildChannelUri(0);
@@ -228,6 +335,7 @@
     }
 
     public void verifyCallbackVideoAvailable() {
+        resetCounts();
         CountingSession session = CountingTvInputService.sSession;
         assertNotNull(session);
         session.notifyVideoAvailable();
@@ -240,6 +348,7 @@
     }
 
     public void verifyCallbackVideoUnavailable() {
+        resetCounts();
         CountingSession session = CountingTvInputService.sSession;
         assertNotNull(session);
         session.notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
@@ -252,6 +361,7 @@
     }
 
     public void verifyCallbackTracksChanged() {
+        resetCounts();
         CountingSession session = CountingTvInputService.sSession;
         assertNotNull(session);
         ArrayList<TvTrackInfo> tracks = new ArrayList<>();
@@ -265,7 +375,23 @@
         }.run();
     }
 
+    public void verifyCallbackVideoSizeChanged() {
+        resetCounts();
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        ArrayList<TvTrackInfo> tracks = new ArrayList<>();
+        tracks.add(mDummyTrack);
+        session.notifyTracksChanged(tracks);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mCallback.mVideoSizeChanged > 0;
+            }
+        }.run();
+    }
+
     public void verifyCallbackTrackSelected() {
+        resetCounts();
         CountingSession session = CountingTvInputService.sSession;
         assertNotNull(session);
         session.notifyTrackSelected(mDummyTrack.getType(), mDummyTrack.getId());
@@ -278,6 +404,7 @@
     }
 
     public void verifyCallbackContentAllowed() {
+        resetCounts();
         CountingSession session = CountingTvInputService.sSession;
         assertNotNull(session);
         session.notifyContentAllowed();
@@ -290,6 +417,7 @@
     }
 
     public void verifyCallbackContentBlocked() {
+        resetCounts();
         CountingSession session = CountingTvInputService.sSession;
         assertNotNull(session);
         TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
@@ -303,13 +431,20 @@
         }.run();
     }
 
+    private void resetCounts() {
+        if (CountingTvInputService.sSession != null) {
+            CountingTvInputService.sSession.resetCounts();
+        }
+        mCallback.resetCounts();
+    }
+
     public static class CountingTvInputService extends StubTvInputService {
-        static CountingTvInputService sInstance;
         static CountingSession sSession;
 
         @Override
         public Session onCreateSession(String inputId) {
             sSession = new CountingSession(this);
+            sSession.setOverlayViewEnabled(true);
             return sSession;
         }
 
@@ -318,12 +453,34 @@
             public volatile int mSetStreamVolumeCount;
             public volatile int mSetCaptionEnabledCount;
             public volatile int mSelectTrackCount;
+            public volatile int mCreateOverlayView;
             public volatile int mKeyDownCount;
+            public volatile int mKeyLongPressCount;
+            public volatile int mKeyMultipleCount;
+            public volatile int mKeyUpCount;
+            public volatile int mTouchEventCount;
+            public volatile int mTrackballEventCount;
+            public volatile int mGenricMotionEventCount;
 
             CountingSession(Context context) {
                 super(context);
             }
 
+            public void resetCounts() {
+                mTuneCount = 0;
+                mSetStreamVolumeCount = 0;
+                mSetCaptionEnabledCount = 0;
+                mSelectTrackCount = 0;
+                mCreateOverlayView = 0;
+                mKeyDownCount = 0;
+                mKeyLongPressCount = 0;
+                mKeyMultipleCount = 0;
+                mKeyUpCount = 0;
+                mTouchEventCount = 0;
+                mTrackballEventCount = 0;
+                mGenricMotionEventCount = 0;
+            }
+
             @Override
             public void onRelease() {
             }
@@ -356,10 +513,52 @@
             }
 
             @Override
+            public View onCreateOverlayView() {
+                mCreateOverlayView++;
+                return null;
+            }
+
+            @Override
             public boolean onKeyDown(int keyCode, KeyEvent event) {
                 mKeyDownCount++;
                 return false;
             }
+
+            @Override
+            public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+                mKeyLongPressCount++;
+                return false;
+            }
+
+            @Override
+            public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+                mKeyMultipleCount++;
+                return false;
+            }
+
+            @Override
+            public boolean onKeyUp(int keyCode, KeyEvent event) {
+                mKeyUpCount++;
+                return false;
+            }
+
+            @Override
+            public boolean onTouchEvent(MotionEvent event) {
+                mTouchEventCount++;
+                return false;
+            }
+
+            @Override
+            public boolean onTrackballEvent(MotionEvent event) {
+                mTrackballEventCount++;
+                return false;
+            }
+
+            @Override
+            public boolean onGenericMotionEvent(MotionEvent event) {
+                mGenricMotionEventCount++;
+                return false;
+            }
         }
     }
 }
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java b/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java
new file mode 100644
index 0000000..a99bd77
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.cts;
+
+import android.media.tv.TvTrackInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+/**
+ * Test {@link android.media.tv.TvTrackInfo}.
+ */
+public class TvTrackInfoTest extends AndroidTestCase {
+
+    public void testAudioTrackInfoOp() {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final Bundle bundle = new Bundle();
+        final TvTrackInfo info = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "id_audio")
+                .setAudioChannelCount(2)
+                .setAudioSampleRate(48000)
+                .setLanguage("eng")
+                .setExtra(bundle)
+                .build();
+        assertEquals(TvTrackInfo.TYPE_AUDIO, info.getType());
+        assertEquals("id_audio", info.getId());
+        assertEquals(2, info.getAudioChannelCount());
+        assertEquals(48000, info.getAudioSampleRate());
+        assertEquals("eng", info.getLanguage());
+        assertEquals(bundle.get("testTrue"), info.getExtra().get("testTrue"));
+        assertEquals(0, info.describeContents());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        TvTrackInfo infoFromParcel = TvTrackInfo.CREATOR.createFromParcel(p);
+        assertEquals(TvTrackInfo.TYPE_AUDIO, infoFromParcel.getType());
+        assertEquals("id_audio", infoFromParcel.getId());
+        assertEquals(2, infoFromParcel.getAudioChannelCount());
+        assertEquals(48000, infoFromParcel.getAudioSampleRate());
+        assertEquals("eng", infoFromParcel.getLanguage());
+        assertEquals(bundle.get("testTrue"), infoFromParcel.getExtra().get("testTrue"));
+        assertEquals(0, infoFromParcel.describeContents());
+        p.recycle();
+    }
+
+    public void testVideoTrackInfoOp() {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean("testTrue", true);
+        final TvTrackInfo info = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "id_video")
+                .setVideoWidth(1920)
+                .setVideoHeight(1080)
+                .setVideoFrameRate(29.97f)
+                .setLanguage("eng")
+                .setExtra(bundle)
+                .build();
+        assertEquals(TvTrackInfo.TYPE_VIDEO, info.getType());
+        assertEquals("id_video", info.getId());
+        assertEquals(1920, info.getVideoWidth());
+        assertEquals(1080, info.getVideoHeight());
+        assertEquals(29.97f, info.getVideoFrameRate());
+        assertEquals("eng", info.getLanguage());
+        assertEquals(bundle.get("testTrue"), info.getExtra().get("testTrue"));
+        assertEquals(0, info.describeContents());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        TvTrackInfo infoFromParcel = TvTrackInfo.CREATOR.createFromParcel(p);
+        assertEquals(TvTrackInfo.TYPE_VIDEO, infoFromParcel.getType());
+        assertEquals("id_video", infoFromParcel.getId());
+        assertEquals(1920, infoFromParcel.getVideoWidth());
+        assertEquals(1080, infoFromParcel.getVideoHeight());
+        assertEquals(29.97f, infoFromParcel.getVideoFrameRate());
+        assertEquals("eng", infoFromParcel.getLanguage());
+        assertEquals(bundle.get("testTrue"), infoFromParcel.getExtra().get("testTrue"));
+        assertEquals(0, infoFromParcel.describeContents());
+        p.recycle();
+    }
+
+    public void testSubtitleTrackInfoOp() {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean("testTrue", true);
+        final TvTrackInfo info = new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "id_subtitle")
+                .setLanguage("eng")
+                .setExtra(bundle)
+                .build();
+        assertEquals(TvTrackInfo.TYPE_SUBTITLE, info.getType());
+        assertEquals("id_subtitle", info.getId());
+        assertEquals("eng", info.getLanguage());
+        assertEquals(bundle.get("testTrue"), info.getExtra().get("testTrue"));
+        assertEquals(0, info.describeContents());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        TvTrackInfo infoFromParcel = TvTrackInfo.CREATOR.createFromParcel(p);
+        assertEquals(TvTrackInfo.TYPE_SUBTITLE, infoFromParcel.getType());
+        assertEquals("id_subtitle", infoFromParcel.getId());
+        assertEquals("eng", infoFromParcel.getLanguage());
+        assertEquals(bundle.get("testTrue"), infoFromParcel.getExtra().get("testTrue"));
+        assertEquals(0, infoFromParcel.describeContents());
+        p.recycle();
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
index 930dd6a..6232861 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
@@ -47,7 +47,7 @@
  */
 public class TvViewTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
     /** The maximum time to wait for an operation. */
-    private static final long TIME_OUT = 15000L;
+    private static final long TIME_OUT_MS = 15000L;
 
     private TvView mTvView;
     private Activity mActivity;
@@ -61,6 +61,7 @@
         private final Map<String, SparseIntArray> mSelectedTrackGenerationMap = new ArrayMap<>();
         private final Map<String, Integer> mTracksGenerationMap = new ArrayMap<>();
         private final Object mLock = new Object();
+        private volatile int mConnectionFailedCount;
 
         public boolean isVideoAvailable(String inputId) {
             synchronized (mLock) {
@@ -80,6 +81,19 @@
             }
         }
 
+        public void resetConnectionFailedCount() {
+            mConnectionFailedCount = 0;
+        }
+
+        public int getConnectionFailedCount() {
+            return mConnectionFailedCount;
+        }
+
+        @Override
+        public void onConnectionFailed(String inputId) {
+            mConnectionFailedCount++;
+        }
+
         @Override
         public void onVideoAvailable(String inputId) {
             synchronized (mLock) {
@@ -201,7 +215,7 @@
                 Uri channelUri = TvContract.buildChannelUri(channelId);
                 mTvView.tune(mStubInfo.getId(), channelUri);
                 mInstrumentation.waitForIdleSync();
-                new PollingCheck(TIME_OUT) {
+                new PollingCheck(TIME_OUT_MS) {
                     @Override
                     protected boolean check() {
                         return mCallback.isVideoAvailable(mStubInfo.getId());
@@ -232,7 +246,7 @@
         if ((track == null && selectedTrackId != null)
                 || (track != null && !track.getId().equals(selectedTrackId))) {
             // Check generation change only if we're actually changing track.
-            new PollingCheck(TIME_OUT) {
+            new PollingCheck(TIME_OUT_MS) {
                 @Override
                 protected boolean check() {
                     return mCallback.getSelectedTrackGeneration(
@@ -303,7 +317,7 @@
         tryTuneAllChannels(new Runnable() {
             @Override
             public void run() {
-                new PollingCheck(TIME_OUT) {
+                new PollingCheck(TIME_OUT_MS) {
                     @Override
                     protected boolean check() {
                         return mTvView.getTracks(TvTrackInfo.TYPE_AUDIO) != null;
@@ -325,7 +339,7 @@
         unhandledEvent[0] = null;
         mInstrumentation.sendKeySync(keyEvent);
         mInstrumentation.waitForIdleSync();
-        new PollingCheck(TIME_OUT) {
+        new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
                 return unhandledEvent[0] != null;
@@ -362,7 +376,7 @@
             Uri channelUri = TvContract.buildChannelUri(channelId);
             mTvView.tune(mStubInfo.getId(), channelUri);
             mInstrumentation.waitForIdleSync();
-            new PollingCheck(TIME_OUT) {
+            new PollingCheck(TIME_OUT_MS) {
                 @Override
                 protected boolean check() {
                     return mCallback.isVideoAvailable(mStubInfo.getId());
@@ -382,4 +396,19 @@
         verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
         verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
     }
+
+    public void testConnectionFailed() throws Throwable {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        mCallback.resetConnectionFailedCount();
+        mTvView.tune("invalid_input_id", TvContract.Channels.CONTENT_URI);
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return mCallback.getConnectionFailedCount() > 0;
+            }
+        }.run();
+    }
 }
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
old mode 100644
new mode 100755
index d267d63..3c6028f
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -115,6 +115,9 @@
             // Wait for things to settle.
             getUiDevice().waitForIdle();
 
+            // Wait for Activity draw finish
+            getInstrumentation().waitForIdleSync();
+
             // Find the application window.
             final int windowId = findAppWindowId(uiAutomation.getWindows());
             assertTrue(windowId >= 0);
diff --git a/tests/tests/uirendering/res/layout/blue_padded_layout.xml b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
index 68c9cd1..2bfd049 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_layout.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
@@ -14,16 +14,15 @@
        limitations under the License.
   -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/test_root"
-        android:layout_width="200px"
-        android:layout_height="200px"
-        android:clipChildren="false">
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"
+    android:clipChildren="false">
     <android.uirendering.cts.testclasses.view.UnclippedBlueView
-            android:id="@+id/child"
-            android:layout_width="100px"
-            android:layout_height="100px"
-            android:paddingLeft="15px"
-            android:paddingTop="16px"
-            android:paddingRight="17px"
-            android:paddingBottom="18px"/>
+        android:id="@+id/child"
+        android:layout_width="80px"
+        android:layout_height="80px"
+        android:paddingLeft="15px"
+        android:paddingTop="16px"
+        android:paddingRight="17px"
+        android:paddingBottom="18px"/>
 </FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/blue_padded_square.xml b/tests/tests/uirendering/res/layout/blue_padded_square.xml
index 71f4b0c..7d867d9 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_square.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_square.xml
@@ -14,7 +14,6 @@
        limitations under the License.
   -->
 <View xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/test_root"
-    android:layout_width="100px"
-    android:layout_height="100px"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"
     android:background="@drawable/blue_padded_square"/>
diff --git a/tests/tests/uirendering/res/layout/simple_rect_layout.xml b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
index b570df8..7d6f928 100644
--- a/tests/tests/uirendering/res/layout/simple_rect_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
@@ -13,14 +13,12 @@
        See the License for the specific language governing permissions and
        limitations under the License.
   -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/test_root"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <View android:layout_width="100px"
-        android:layout_height="100px"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height">
+    <View android:layout_width="80px"
+        android:layout_height="80px"
+        android:translationX="5px"
+        android:translationY="5px"
         android:background="#00f" />
-
-</LinearLayout>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/simple_red_layout.xml b/tests/tests/uirendering/res/layout/simple_red_layout.xml
index 2d2d189..2af8db6 100644
--- a/tests/tests/uirendering/res/layout/simple_red_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_red_layout.xml
@@ -14,7 +14,7 @@
        limitations under the License.
   -->
 <View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"
     android:id="@+id/test_root"
-    android:layout_width="180px"
-    android:layout_height="180px"
     android:background="#f00" />
diff --git a/tests/tests/uirendering/res/layout/test_container.xml b/tests/tests/uirendering/res/layout/test_container.xml
new file mode 100644
index 0000000..94a8eab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_container.xml
@@ -0,0 +1,24 @@
+<!-- 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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/test_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ViewStub
+        android:id="@+id/test_content_stub"
+        android:layout_gravity="center"
+        android:layout_width="@dimen/test_width"
+        android:layout_height="@dimen/test_height"/>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
new file mode 100644
index 0000000..9f8a139
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
@@ -0,0 +1,18 @@
+<!-- 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.uirendering.cts.testinfrastructure.CanvasClientView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/layout/test_content_webview.xml b/tests/tests/uirendering/res/layout/test_content_webview.xml
new file mode 100644
index 0000000..8b03cab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_webview.xml
@@ -0,0 +1,18 @@
+<!-- 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.
+  -->
+<WebView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/values/dimens.xml b/tests/tests/uirendering/res/values/dimens.xml
new file mode 100644
index 0000000..1c304d3
--- /dev/null
+++ b/tests/tests/uirendering/res/values/dimens.xml
@@ -0,0 +1,18 @@
+<!-- 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.
+  -->
+<resources>
+    <dimen name="test_width">90px</dimen>
+    <dimen name="test_height">90px</dimen>
+</resources>
diff --git a/tests/tests/uirendering/res/values/themes.xml b/tests/tests/uirendering/res/values/themes.xml
index 751b7cb..f08f029 100644
--- a/tests/tests/uirendering/res/values/themes.xml
+++ b/tests/tests/uirendering/res/values/themes.xml
@@ -17,9 +17,9 @@
     <style name="WhiteBackgroundTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
         <item name="android:fadingEdge">none</item>
         <item name="android:windowBackground">@android:color/white</item>
-        <!--This shouldn't be necessary currently a hack for an existing bug with transitions-->
         <item name="android:windowContentTransitions">false</item>
         <item name="android:windowAnimationStyle">@null</item>
     </style>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
index e766f63..56d9cf5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
@@ -15,7 +15,6 @@
  */
 package android.uirendering.cts.bitmapcomparers;
 
-import com.android.cts.uirendering.R;
 import com.android.cts.uirendering.ScriptC_MSSIMComparer;
 
 import android.content.res.Resources;
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
index ea836ea..7f62b3e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
@@ -22,11 +22,16 @@
     private int mColor;
 
     public ColorVerifier(int color) {
-        this(color, 20);
+        this(color, DEFAULT_THRESHOLD);
     }
 
-    public ColorVerifier(int color, int threshold) {
-        super(threshold);
+    public ColorVerifier(int color, int colorTolerance) {
+        super(colorTolerance);
+        mColor = color;
+    }
+
+    public ColorVerifier(int color, int colorThreshold, float spatialTolerance) {
+        super(colorThreshold, spatialTolerance);
         mColor = color;
     }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
index 672b3f6..d4a63de 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -18,6 +18,7 @@
 import android.graphics.Bitmap;
 import android.uirendering.cts.bitmapcomparers.BitmapComparer;
 import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 
 public class GoldenImageVerifier extends BitmapVerifier {
     private BitmapComparer mBitmapComparer;
@@ -26,7 +27,7 @@
     public GoldenImageVerifier(Bitmap goldenBitmap, BitmapComparer bitmapComparer) {
         mGoldenBitmapArray = new int[goldenBitmap.getWidth() * goldenBitmap.getHeight()];
         goldenBitmap.getPixels(mGoldenBitmapArray, 0, goldenBitmap.getWidth(), 0, 0,
-                goldenBitmap.getWidth(), goldenBitmap.getHeight());
+                ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
         mBitmapComparer = bitmapComparer;
     }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
index 0bdcc9b..2d00db5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -26,38 +26,54 @@
  */
 public abstract class PerPixelBitmapVerifier extends BitmapVerifier {
     private static final String TAG = "PerPixelBitmapVerifer";
-    private int mTolerance;
+    public static final int DEFAULT_THRESHOLD = 48;
 
-    public PerPixelBitmapVerifier(int tolerance) {
-        mTolerance = tolerance;
+    // total color difference tolerated without the pixel failing
+    private int mColorTolerance;
+
+    // portion of bitmap allowed to fail pixel check
+    private float mSpatialTolerance;
+
+    public PerPixelBitmapVerifier() {
+        this(DEFAULT_THRESHOLD, 0);
+    }
+
+    public PerPixelBitmapVerifier(int colorTolerance) {
+        this(colorTolerance, 0);
+    }
+
+    public PerPixelBitmapVerifier(int colorTolerance, float spatialTolerance) {
+        mColorTolerance = colorTolerance;
+        mSpatialTolerance = spatialTolerance;
     }
 
     protected int getExpectedColor(int x, int y) {
         return Color.WHITE;
     }
 
-
     public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
-        int failCount = 0;
+        int failures = 0;
         int[] differenceMap = new int[bitmap.length];
         for (int y = 0 ; y < height ; y++) {
             for (int x = 0 ; x < width ; x++) {
                 int index = indexFromXAndY(x, y, stride, offset);
-                int expectedColor = getExpectedColor(x, y);
-                if (!verifyPixel(bitmap[index], expectedColor)) {
-                    if (failCount < 50) {
-                        Log.d(TAG, "Expected : " + Integer.toHexString(expectedColor)
+                if (!verifyPixel(x, y, bitmap[index])) {
+                    if (failures < 50) {
+                        Log.d(TAG, "Expected : " + Integer.toHexString(getExpectedColor(x, y))
                                 + " received : " + Integer.toHexString(bitmap[index])
                                 + " at position (" + x + "," + y + ")");
                     }
-                    failCount++;
+                    failures++;
                     differenceMap[index] = FAIL_COLOR;
                 } else {
                     differenceMap[index] = PASS_COLOR;
                 }
             }
         }
-        boolean success = failCount == 0;
+        int toleratedFailures = (int) (mSpatialTolerance * width * height);
+        boolean success = failures <= toleratedFailures;
+        Log.d(TAG, failures + " failures observed out of "
+                + toleratedFailures + " tolerated failures");
         if (!success) {
             mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
                     ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
@@ -67,7 +83,9 @@
         return success;
     }
 
-    protected boolean verifyPixel(int color, int expectedColor) {
-        return CompareUtils.verifyPixelWithThreshold(color, expectedColor, mTolerance);
+
+    protected boolean verifyPixel(int x, int y, int observedColor) {
+        int expectedColor = getExpectedColor(x, y);
+        return CompareUtils.verifyPixelWithThreshold(observedColor, expectedColor, mColorTolerance);
     }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
index 06a430b..f4bece1 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
@@ -26,7 +26,7 @@
     private Rect mInnerRect;
 
     public RectVerifier(int outerColor, int innerColor, Rect innerRect) {
-        this(outerColor, innerColor, innerRect, 20);
+        this(outerColor, innerColor, innerRect, DEFAULT_THRESHOLD);
     }
 
     public RectVerifier(int outerColor, int innerColor, Rect innerRect, int tolerance) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
index ddae100..1d5cd16 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
@@ -28,7 +28,6 @@
 import android.uirendering.cts.testinfrastructure.CanvasClient;
 
 public class BitmapFilterTests extends ActivityTestBase {
-    private static final int THRESHOLD = 20;
     private static final int WHITE_WEIGHT = 255 * 3;
     private enum FilterEnum {
         // Creates Paint object that will have bitmap filtering
@@ -40,72 +39,77 @@
         ADD_FILTER
     }
 
-    /**
-     * Verifies that a Bitmap only contains white and black, within a certain threshold
-     */
-    private static BitmapVerifier mBlackWhiteVerifier = new PerPixelBitmapVerifier(THRESHOLD) {
+    private static final BitmapVerifier BLACK_WHITE_ONLY_VERIFIER
+            = new PerPixelBitmapVerifier(PerPixelBitmapVerifier.DEFAULT_THRESHOLD, 0.99f) {
         @Override
-        protected boolean verifyPixel(int color, int expectedColor) {
+        protected boolean verifyPixel(int x, int y, int color) {
             int weight = Color.red(color) + Color.blue(color) + Color.green(color);
-            return weight < THRESHOLD // is approx Color.BLACK
-                    || weight > WHITE_WEIGHT - THRESHOLD; // is approx Color.WHITE
+            return weight < DEFAULT_THRESHOLD // is approx Color.BLACK
+                    || weight > WHITE_WEIGHT - DEFAULT_THRESHOLD; // is approx Color.WHITE
         }
     };
+    private static final BitmapVerifier GREY_ONLY_VERIFIER
+            = new ColorVerifier(Color.argb(255, 127, 127, 127),
+            150); // content will be entirely grey, for a fairly wide range of grey
+    private static final BitmapVerifier GREY_PARTIAL_VERIFIER
+            = new ColorVerifier(Color.argb(255, 127, 127, 127),
+            300, 0.8f); // content will be mostly grey, for a wide range of grey
+
 
     private static Bitmap createGridBitmap(int width, int height) {
         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         for (int i = 0 ; i < width ; i++) {
             for (int j = 0 ; j < height ; j++) {
-                bitmap.setPixel(i, j, ((i + j * width)) % 2 == 0 ?
-                        Color.WHITE : Color.BLACK);
+                boolean isWhite = (i + j * width) % 2 == 0;
+                bitmap.setPixel(i, j, isWhite ? Color.WHITE : Color.BLACK);
             }
         }
         return bitmap;
     }
 
     private static final int SMALL_GRID_SIZE = 5;
-    private static Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
-    private static final int BIG_GRID_SIZE = 360;
-    private static Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
-    private static final int HALFWAY_COLOR = Color.argb(255, 127, 127, 127);
+    private Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
 
-    /* All of these tests seem to be broken.
-     * TODO: fix in L MR1
+    // samples will occur in the middle of 4 pixels, 2 white
+    // and 2 black, and thus each be close to x7F7F7F grey
+    private static final int BIG_GRID_SIZE = TEST_WIDTH * 2;
+    private Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
+
     @SmallTest
     public void testPaintFilterScaleUp() {
-        runScaleTest(FilterEnum.PAINT_FILTER, true, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.PAINT_FILTER, true);
     }
 
     @SmallTest
     public void testPaintFilterScaleDown() {
-        runScaleTest(FilterEnum.PAINT_FILTER, false, new ColorVerifier(HALFWAY_COLOR, 15));
+        runScaleTest(FilterEnum.PAINT_FILTER, false);
     }
 
     @SmallTest
     public void testDrawFilterRemoveFilterScaleUp() {
-        runScaleTest(FilterEnum.REMOVE_FILTER, true, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.REMOVE_FILTER, true);
     }
 
     @SmallTest
     public void testDrawFilterRemoveFilterScaleDown() {
-        runScaleTest(FilterEnum.REMOVE_FILTER, false, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.REMOVE_FILTER, false);
     }
 
     @SmallTest
     public void testDrawFilterScaleUp() {
-        runScaleTest(FilterEnum.ADD_FILTER, true, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.ADD_FILTER, true);
     }
 
     @SmallTest
     public void testDrawFilterScaleDown() {
-        runScaleTest(FilterEnum.ADD_FILTER, false, new ColorVerifier(HALFWAY_COLOR));
+        runScaleTest(FilterEnum.ADD_FILTER, false);
     }
-*/
-    private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp,
-            BitmapVerifier bitmapVerifier) {
+
+    private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp) {
         final int gridWidth = scaleUp ? SMALL_GRID_SIZE : BIG_GRID_SIZE;
         final Paint paint = new Paint(filterEnum.equals(FilterEnum.ADD_FILTER) ?
                 0 : Paint.FILTER_BITMAP_FLAG);
+
         CanvasClient canvasClient = new CanvasClient() {
             @Override
             public void draw(Canvas canvas, int width, int height) {
@@ -120,6 +124,16 @@
         };
         createTest()
                 .addCanvasClient(canvasClient)
-                .runWithVerifier(bitmapVerifier);
+                .runWithVerifier(getVerifierForTest(filterEnum, scaleUp));
+    }
+
+    private static BitmapVerifier getVerifierForTest(FilterEnum filterEnum, boolean scaleUp) {
+        if (filterEnum.equals(FilterEnum.REMOVE_FILTER)) {
+            // filtering disabled, so only black and white pixels will come through
+            return BLACK_WHITE_ONLY_VERIFIER;
+        }
+        // if scaling up, output pixels may have single source to sample from,
+        // will only be *mostly* grey.
+        return scaleUp ? GREY_PARTIAL_VERIFIER : GREY_ONLY_VERIFIER;
     }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index afbad65..29755d8 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -36,7 +36,7 @@
 
     @SmallTest
     public void testBlueRect() {
-        final Rect rect = new Rect(10, 10, 100, 100);
+        final Rect rect = new Rect(10, 10, 80, 80);
         createTest()
                 .addCanvasClient(new CanvasClient() {
                     @Override
@@ -58,8 +58,6 @@
                     public void draw(Canvas canvas, int width, int height) {
                         Paint p = new Paint();
                         p.setAntiAlias(false);
-                        p.setColor(Color.WHITE);
-                        canvas.drawRect(0, 0, 100, 100, p);
                         p.setStrokeWidth(1f);
                         p.setColor(Color.BLACK);
                         for (int i = 0; i < 10; i++) {
@@ -81,8 +79,8 @@
                         canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
                                 ActivityTestBase.TEST_HEIGHT, p);
                         p.setColor(Color.BLACK);
-                        p.setStrokeWidth(10);
-                        canvas.drawRect(10, 10, 20, 20, p);
+                        p.setStrokeWidth(5);
+                        canvas.drawRect(10, 10, 80, 80, p);
                     }
                 })
                 .runWithComparer(mExactComparer);
@@ -94,10 +92,8 @@
                 .addCanvasClient(new CanvasClient() {
                     @Override
                     public void draw(Canvas canvas, int width, int height) {
+                        canvas.drawColor(Color.GREEN);
                         Paint p = new Paint();
-                        p.setColor(Color.GREEN);
-                        canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
-                                ActivityTestBase.TEST_HEIGHT, p);
                         p.setColor(Color.BLACK);
                         p.setStrokeWidth(10);
                         canvas.drawLine(0, 0, 50, 0, p);
@@ -131,7 +127,7 @@
                         canvas.drawColor(Color.WHITE);
                         p.setColor(Color.BLACK);
                         float[] pts = {
-                                0, 0, 100, 100, 100, 0, 0, 100, 50, 50, 75, 75
+                                0, 0, 80, 80, 80, 0, 0, 80, 40, 50, 60, 50
                         };
                         canvas.drawLines(pts, p);
                     }
@@ -185,10 +181,10 @@
     public void testBluePaddedSquare() {
         final NinePatchDrawable ninePatchDrawable = (NinePatchDrawable)
             getActivity().getResources().getDrawable(R.drawable.blue_padded_square);
-        ninePatchDrawable.setBounds(0, 0, 100, 100);
+        ninePatchDrawable.setBounds(0, 0, 90, 90);
 
         BitmapVerifier verifier = new RectVerifier(Color.WHITE, Color.BLUE,
-                new Rect(10, 10, 90, 90));
+                new Rect(10, 10, 80, 80));
 
         createTest()
                 .addCanvasClient(new CanvasClient() {
@@ -197,7 +193,7 @@
                         canvas.drawColor(Color.WHITE);
                         Paint p = new Paint();
                         p.setColor(Color.BLUE);
-                        canvas.drawRect(10, 10, 90, 90, p);
+                        canvas.drawRect(10, 10, 80, 80, p);
                     }
                 })
                 .addCanvasClient(new CanvasClient() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
index 6662226..c86ff76 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
@@ -15,21 +15,19 @@
  */
 package android.uirendering.cts.testclasses;
 
+import android.graphics.Point;
 import com.android.cts.uirendering.R;
 
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.uirendering.cts.bitmapcomparers.BitmapComparer;
-import android.uirendering.cts.bitmapcomparers.ExactComparer;
 import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
 import android.uirendering.cts.bitmapverifiers.RectVerifier;
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 import android.uirendering.cts.testinfrastructure.CanvasClient;
 import android.uirendering.cts.testinfrastructure.ViewInitializer;
-import android.util.Log;
 import android.view.View;
 
 public class InfrastructureTests extends ActivityTestBase {
@@ -37,7 +35,7 @@
     @SmallTest
     public void testScreenshot() {
         for (int i = 0 ; i < 500 ; i ++) {
-            takeScreenshot();
+            takeScreenshot(new Point());
             System.gc();
         }
     }
@@ -52,16 +50,17 @@
         CanvasClient canvasClient = new CanvasClient() {
             @Override
             public void draw(Canvas canvas, int width, int height) {
-                canvas.drawColor(canvas.isHardwareAccelerated() ? Color.WHITE : Color.BLACK);
+                canvas.drawColor(canvas.isHardwareAccelerated() ? Color.BLACK : Color.WHITE);
             }
         };
-        // This is considered a very high threshold and as such, the test should still fail because
-        // they are completely different images.
-        final float threshold = 0.1f;
         BitmapComparer inverseComparer = new BitmapComparer() {
             @Override
             public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
                     int height) {
+
+                // Return true if the images aren't even 10% similar. They should be completely
+                // different, since they should both be completely different colors.
+                final float threshold = 0.1f;
                 return !(new MSSIMComparer(threshold)).verifySame(ideal, given, offset, stride,
                         width, height);
             }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
index 4667ee9..ff1e9db 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
@@ -34,7 +34,7 @@
     @SmallTest
     public void testSimpleRectLayout() {
         createTest().addLayout(R.layout.simple_rect_layout, null, false).runWithVerifier(
-                new RectVerifier(Color.WHITE, Color.BLUE, new Rect(0, 0, 100, 100)));
+                new RectVerifier(Color.WHITE, Color.BLUE, new Rect(5, 5, 85, 85)));
     }
 }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 8df8057..6911cf0 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -17,7 +17,7 @@
 import com.android.cts.uirendering.R;
 
 public class PathClippingTests extends ActivityTestBase {
-    // draw circle with whole in it, with stroked circle
+    // draw circle with hole in it, with stroked circle
     static final CanvasClient sCircleDrawCanvasClient = new CanvasClient() {
         @Override
         public String getDebugString() {
@@ -31,11 +31,11 @@
             paint.setColor(Color.BLUE);
             paint.setStyle(Paint.Style.STROKE);
             paint.setStrokeWidth(20);
-            canvas.drawCircle(50, 50, 40, paint);
+            canvas.drawCircle(30, 30, 40, paint);
         }
     };
 
-    // draw circle with whole in it, by path operations + path clipping
+    // draw circle with hole in it, by path operations + path clipping
     static final CanvasClient sCircleClipCanvasClient = new CanvasClient() {
         @Override
         public String getDebugString() {
@@ -47,8 +47,8 @@
             canvas.save();
 
             Path path = new Path();
-            path.addCircle(50, 50, 50, Path.Direction.CW);
-            path.addCircle(50, 50, 30, Path.Direction.CCW);
+            path.addCircle(30, 30, 50, Path.Direction.CW);
+            path.addCircle(30, 30, 30, Path.Direction.CCW);
 
             canvas.clipPath(path);
             canvas.drawColor(Color.BLUE);
@@ -72,12 +72,12 @@
                 .runWithVerifier(new SamplePointVerifier(
                         new Point[] {
                                 // inside of circle
-                                new Point(50, 50),
+                                new Point(30, 50),
                                 // on circle
-                                new Point(50 + 32, 50 + 32),
+                                new Point(30 + 32, 30 + 32),
                                 // outside of circle
-                                new Point(50 + 38, 50 + 38),
-                                new Point(100, 100)
+                                new Point(30 + 38, 30 + 38),
+                                new Point(80, 80)
                         },
                         new int[] {
                                 Color.WHITE,
@@ -96,8 +96,8 @@
                         ViewGroup rootView = (ViewGroup) view;
                         rootView.setClipChildren(true);
                         View childView = rootView.getChildAt(0);
-                        childView.setPivotX(50);
-                        childView.setPivotY(50);
+                        childView.setPivotX(40);
+                        childView.setPivotY(40);
                         childView.setRotation(45f);
 
                     }
@@ -105,11 +105,11 @@
                 .runWithVerifier(new SamplePointVerifier(
                         new Point[] {
                                 // inside of rotated rect
-                                new Point(50, 50),
-                                new Point(50 + 32, 50 + 32),
+                                new Point(40, 40),
+                                new Point(40 + 25, 40 + 25),
                                 // outside of rotated rect
-                                new Point(50 + 38, 50 + 38),
-                                new Point(100, 100)
+                                new Point(40 + 31, 40 + 31),
+                                new Point(80, 80)
                         },
                         new int[] {
                                 Color.BLUE,
@@ -128,15 +128,15 @@
                         canvas.save();
 
                         Path path = new Path();
-                        path.addCircle(0, 50, 50, Path.Direction.CW);
-                        path.addCircle(100, 50, 50, Path.Direction.CW);
+                        path.addCircle(0, 45, 45, Path.Direction.CW);
+                        path.addCircle(90, 45, 45, Path.Direction.CW);
                         canvas.clipPath(path);
 
                         Paint paint = new Paint();
                         paint.setAntiAlias(true);
-                        paint.setTextSize(100);
+                        paint.setTextSize(90);
                         paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
-                        canvas.drawText("STRING", 0, 100, paint);
+                        canvas.drawText("STRING", 0, 90, paint);
 
                         canvas.restore();
                     }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
index 7947286..32ab0e4 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
@@ -22,6 +22,7 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Shader;
@@ -50,17 +51,11 @@
     public static final int MULTIPLY_COLOR = 0xFF668844;
     public static final int SCREEN_COLOR = 0xFFFFEEFF;
 
-    public static final int FILTER_COLOR = 0xFFBB0000;
-    public static final int RECT0_COLOR = 0x33808080;
-    public static final int RECT1_COLOR = 0x66808080;
-    public static final int RECT2_COLOR = 0x99808080;
-    public static final int RECT3_COLOR = 0xCC808080;
-
     // These points are in pairs, the first being the lower left corner, the second is only in the
     // Destination bitmap, the third is the intersection of the two bitmaps, and the fourth is in
     // the Source bitmap.
     private final static Point[] XFERMODE_TEST_POINTS = new Point[] {
-            new Point(1, 160), new Point(50, 50), new Point(70, 70), new Point(140, 140)
+            new Point(1, 80), new Point(25, 25), new Point(35, 35), new Point(70, 70)
     };
 
     /**
@@ -128,8 +123,8 @@
     };
 
     private final static DisplayModifier XFERMODE_MODIFIER = new DisplayModifier() {
-        private final RectF mSrcRect = new RectF(60, 60, 160, 160);
-        private final RectF mDstRect = new RectF(20, 20, 120, 120);
+        private final RectF mSrcRect = new RectF(30, 30, 80, 80);
+        private final RectF mDstRect = new RectF(10, 10, 60, 60);
         private final Bitmap mSrcBitmap = createSrc();
         private final Bitmap mDstBitmap = createDst();
 
@@ -144,8 +139,7 @@
         }
 
         private Bitmap createSrc() {
-            Bitmap srcB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap srcB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Canvas srcCanvas = new Canvas(srcB);
             Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
             srcPaint.setColor(SRC_COLOR);
@@ -154,8 +148,7 @@
         }
 
         private Bitmap createDst() {
-            Bitmap dstB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap dstB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Canvas dstCanvas = new Canvas(dstB);
             Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
             dstPaint.setColor(DST_COLOR);
@@ -164,18 +157,22 @@
         }
     };
 
-
     // We care about one point in each of the four rectangles of different alpha values, as well as
     // the area outside the rectangles
     private final static Point[] COLOR_FILTER_ALPHA_POINTS = new Point[] {
-            new Point(15, 90), new Point(45, 90), new Point(75, 90), new Point(105, 90),
-            new Point(135, 90)
+            new Point(9, 45),
+            new Point(27, 45),
+            new Point(45, 45),
+            new Point(63, 45),
+            new Point(81, 45)
     };
 
-    private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP = new LinkedHashMap<PorterDuff.Mode, int[]>() {
+    public static final int FILTER_COLOR = 0xFFBB0000;
+    private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP
+            = new LinkedHashMap<PorterDuff.Mode, int[]>() {
         {
             put(PorterDuff.Mode.SRC, new int[] {
-                FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
+                    FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
             });
 
             put(PorterDuff.Mode.DST, new int[] {
@@ -228,10 +225,17 @@
         }
     };
 
+    /**
+     * Draws 5 blocks of different color/opacity to be blended against
+     */
     private final static DisplayModifier COLOR_FILTER_ALPHA_MODIFIER = new DisplayModifier() {
-        private final static int mBlockWidths = 30;
-        private final int[] mColorValues = new int[] {RECT0_COLOR, RECT1_COLOR, RECT2_COLOR,
-                RECT3_COLOR};
+        private final int[] BLOCK_COLORS = new int[] {
+                0x33808080,
+                0x66808080,
+                0x99808080,
+                0xCC808080,
+                0x00000000
+        };
 
         private final Bitmap mBitmap = createQuadRectBitmap();
 
@@ -240,13 +244,15 @@
         }
 
         private Bitmap createQuadRectBitmap() {
-            Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Canvas canvas = new Canvas(bitmap);
             Paint paint = new Paint();
-            for (int i = 0 ; i < 4 ; i++) {
-                paint.setColor(mColorValues[i]);
-                canvas.drawRect(i * mBlockWidths, 0, (i + 1) * mBlockWidths, MODIFIER_HEIGHT, paint);
+            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+            final int blockCount = BLOCK_COLORS.length;
+            final int blockWidth = TEST_WIDTH / blockCount;
+            for (int i = 0 ; i < blockCount; i++) {
+                paint.setColor(BLOCK_COLORS[i]);
+                canvas.drawRect(i * blockWidth, 0, (i + 1) * blockWidth, TEST_HEIGHT, paint);
             }
             return bitmap;
         }
@@ -266,10 +272,9 @@
         }
 
         private Bitmap createGradient() {
-            LinearGradient gradient = new LinearGradient(30, 90, 150, 90, mColors, null,
+            LinearGradient gradient = new LinearGradient(15, 45, 75, 45, mColors, null,
                     Shader.TileMode.REPEAT);
-            Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Paint p = new Paint();
             p.setShader(gradient);
             Canvas c = new Canvas(bitmap);
@@ -281,9 +286,7 @@
     public static final DisplayModifier mCircleDrawModifier = new DisplayModifier() {
         @Override
         public void modifyDrawing(Paint paint, Canvas canvas) {
-            canvas.drawCircle(ActivityTestBase.TEST_WIDTH / 2,
-                    ActivityTestBase.TEST_HEIGHT / 2,
-                    ActivityTestBase.TEST_HEIGHT / 2, paint);
+            canvas.drawCircle(TEST_WIDTH / 2, TEST_HEIGHT / 2, TEST_HEIGHT / 2, paint);
         }
     };
 
@@ -343,16 +346,15 @@
     }
 
     /*
-     * TODO: fix this test for L MR1
     @SmallTest
     public void testShaderSweeps() {
-        int mask = DisplayModifier.Accessor.AA_MASK |
-                DisplayModifier.Accessor.SHADER_MASK |
-                DisplayModifier.Accessor.XFERMODE_MASK |
-                DisplayModifier.Accessor.SHAPES_MASK;
+        int mask = DisplayModifier.Accessor.AA_MASK
+                | DisplayModifier.Accessor.SHADER_MASK
+                | DisplayModifier.Accessor.XFERMODE_MASK
+                | DisplayModifier.Accessor.SHAPES_MASK;
         sweepModifiersForMask(mask, null, DEFAULT_MSSIM_COMPARER, null);
     }
-     */
+    */
 
     protected void sweepModifiersForMask(int mask, final DisplayModifier drawOp,
             BitmapComparer[] bitmapComparers, BitmapVerifier[] bitmapVerifiers) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
index da2db48..343228f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -20,11 +20,11 @@
  * Since the layout is blue on a white background, this is always done with a RectVerifier.
  */
 public class ViewClippingTests extends ActivityTestBase {
-    final Rect FULL_RECT = new Rect(0, 0, 200, 200);
-    final Rect BOUNDS_RECT = new Rect(0, 0, 100, 100);
-    final Rect PADDED_RECT = new Rect(15, 16, 83, 82);
-    final Rect OUTLINE_RECT = new Rect(1, 2, 98, 99);
-    final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 70, 80);
+    final Rect FULL_RECT = new Rect(0, 0, 90, 90);
+    final Rect BOUNDS_RECT = new Rect(0, 0, 80, 80);
+    final Rect PADDED_RECT = new Rect(15, 16, 63, 62);
+    final Rect OUTLINE_RECT = new Rect(1, 2, 78, 79);
+    final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 50, 60);
 
     final ViewInitializer BOUNDS_CLIP_INIT = new ViewInitializer() {
         @Override
@@ -61,10 +61,9 @@
         }
     };
 
-    // TODO: attempt to reduce
-    static final int TOLERANCE = 16;
     static BitmapVerifier makeClipVerifier(Rect blueBoundsRect) {
-        return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, TOLERANCE);
+        // very high error tolerance, since all these tests care about is clip alignment
+        return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, 75);
     }
 
     public void testSimpleUnclipped() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
index 7a16e3c..b8935fb 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
@@ -4,7 +4,6 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.FrameLayout;
 
 public class UnclippedBlueView extends FrameLayout {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 783e710..e1c09f5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.graphics.Bitmap;
+import android.graphics.Point;
 import android.renderscript.Allocation;
 import android.renderscript.RenderScript;
 import android.test.ActivityInstrumentationTestCase2;
@@ -41,15 +42,16 @@
     public static final String TAG = "ActivityTestBase";
     public static final boolean DEBUG = false;
     public static final boolean USE_RS = false;
-    public static final int TEST_WIDTH = 180;
-    public static final int TEST_HEIGHT = 180; //The minimum height and width of a device
+
+    //The minimum height and width of a device
+    public static final int TEST_WIDTH = 90;
+    public static final int TEST_HEIGHT = 90;
+
     public static final int MAX_SCREEN_SHOTS = 100;
 
     private int[] mHardwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
     private int[] mSoftwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
     private DifferenceVisualizer mDifferenceVisualizer;
-    private Allocation mIdealAllocation;
-    private Allocation mGivenAllocation;
     private RenderScript mRenderScript;
     private TestCaseBuilder mTestCaseBuilder;
 
@@ -115,16 +117,29 @@
         getActivity().runOnUiThread(finishRunnable);
     }
 
-    public Bitmap takeScreenshot() {
+    static int[] getBitmapPixels(Bitmap bitmap) {
+        int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
+        bitmap.getPixels(pixels, 0, bitmap.getWidth(),
+                0, 0, bitmap.getWidth(), bitmap.getHeight());
+        return pixels;
+    }
+
+    private Bitmap takeScreenshotImpl(Point testOffset) {
+        Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
+        return Bitmap.createBitmap(source, testOffset.x, testOffset.y, TEST_WIDTH, TEST_HEIGHT);
+    }
+
+    public Bitmap takeScreenshot(Point testOffset) {
         getInstrumentation().waitForIdleSync();
-        Bitmap bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+        Bitmap bitmap1 = takeScreenshotImpl(testOffset);
         Bitmap bitmap2;
         int count = 0;
         do  {
             bitmap2 = bitmap1;
-            bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+            bitmap1 = takeScreenshotImpl(testOffset);
             count++;
-        } while (count < MAX_SCREEN_SHOTS && !Arrays.equals(bitmap2.mBuffer, bitmap1.mBuffer));
+        } while (count < MAX_SCREEN_SHOTS &&
+                !Arrays.equals(getBitmapPixels(bitmap2), getBitmapPixels(bitmap1)));
         return bitmap1;
     }
 
@@ -139,10 +154,11 @@
      * Used to execute a specific part of a test and get the resultant bitmap
      */
     protected Bitmap captureRenderSpec(TestCase testCase) {
-        getActivity().enqueueRenderSpecAndWait(testCase.layoutID, testCase.canvasClient,
+        Point testOffset = getActivity().enqueueRenderSpecAndWait(
+                testCase.layoutID, testCase.canvasClient,
                 testCase.webViewUrl, testCase.viewInitializer, testCase.useHardware);
         testCase.wasTestRan = true;
-        return takeScreenshot();
+        return takeScreenshot(testOffset);
     }
 
     /**
@@ -154,12 +170,12 @@
         boolean success;
 
         if (USE_RS && comparer.supportsRenderScript()) {
-            mIdealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
+            Allocation idealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-            mGivenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
+            Allocation givenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-            success = comparer.verifySameRS(getActivity().getResources(), mIdealAllocation,
-                    mGivenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
+            success = comparer.verifySameRS(getActivity().getResources(), idealAllocation,
+                    givenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
         } else {
             bitmap1.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
             bitmap2.getPixels(mHardwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
@@ -213,11 +229,6 @@
          * every test case is tested against it.
          */
         public void runWithComparer(BitmapComparer bitmapComparer) {
-            if (getActivity().getOnWatch()) {
-                Log.d(TAG, getName() + "skipped");
-                return;
-            }
-
             if (mTestCases.size() == 0) {
                 throw new IllegalStateException("Need at least one test to run");
             }
@@ -236,11 +247,6 @@
          * the verifier given.
          */
         public void runWithVerifier(BitmapVerifier bitmapVerifier) {
-            if (getActivity().getOnWatch()) {
-                Log.d(TAG, getName() + "skipped");
-                return;
-            }
-
             if (mTestCases.size() == 0) {
                 throw new IllegalStateException("Need at least one test to run");
             }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
index 92242f0..60127ae 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
@@ -19,6 +19,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.util.AttributeSet;
 import android.view.View;
 
 /**
@@ -26,14 +27,21 @@
  */
 public class CanvasClientView extends View {
     private CanvasClient mCanvasClient;
-    private int mWidth;
-    private int mHeight;
 
-    public CanvasClientView(Context context, CanvasClient canvasClient, int width, int height) {
+    public CanvasClientView(Context context) {
         super(context);
+    }
+
+    public CanvasClientView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CanvasClientView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setCanvasClient(CanvasClient canvasClient) {
         mCanvasClient = canvasClient;
-        mWidth = width;
-        mHeight = height;
     }
 
     @Override
@@ -45,11 +53,11 @@
             paint.setTextSize(20);
             canvas.drawText(s, 200, 200, paint);
         }
-        if (mCanvasClient != null) {
-            canvas.save();
-            canvas.clipRect(0, 0, mWidth, mHeight);
-            mCanvasClient.draw(canvas, mWidth, mHeight);
-            canvas.restore();
-        }
+        if (mCanvasClient == null) throw new IllegalStateException("Canvas client missing");
+
+        canvas.save();
+        canvas.clipRect(0, 0, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        mCanvasClient.draw(canvas, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        canvas.restore();
     }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
index 684293d..b42ac88 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -32,17 +32,16 @@
  * Modifies the canvas and paint objects when called.
  */
 public abstract class DisplayModifier {
-    private static final RectF gRect = new RectF(0, 0, 100, 100);
-    private static final float[] gPts = new float[]{
-            0, 100, 100, 0, 100, 200, 200, 100
+    private static final RectF RECT = new RectF(0, 0, 100, 100);
+    private static final float[] POINTS = new float[]{
+            0, 40, 40, 0, 40, 80, 80, 40
     };
-    private static final float[] gTriPts = new float[]{
-            75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
+    private static final float[] TRIANGLE_POINTS = new float[]{
+            40, 0, 80, 80, 80, 80, 0, 80, 0, 80, 40, 0
     };
-    private static final int NUM_PARALLEL_LINES = 24;
-    private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
-    protected static final int MODIFIER_WIDTH = 180;
-    protected static final int MODIFIER_HEIGHT = 180;
+    private static final int NUM_PARALLEL_LINES = 10;
+    private static final float[] LINES = new float[NUM_PARALLEL_LINES * 8
+            + TRIANGLE_POINTS.length];
 
     public static final PorterDuff.Mode[] PORTERDUFF_MODES = new PorterDuff.Mode[] {
         PorterDuff.Mode.SRC, PorterDuff.Mode.DST, PorterDuff.Mode.SRC_OVER,
@@ -53,25 +52,23 @@
     };
 
     static {
-        int index;
-        for (index = 0; index < gTriPts.length; index++) {
-            gLinePts[index] = gTriPts[index];
-        }
+        System.arraycopy(TRIANGLE_POINTS, 0, LINES, 0, TRIANGLE_POINTS.length);
+        int index = TRIANGLE_POINTS.length;
         float val = 0;
         for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
-            gLinePts[index + 0] = 150;
-            gLinePts[index + 1] = val;
-            gLinePts[index + 2] = 300;
-            gLinePts[index + 3] = val;
+            LINES[index + 0] = 40;
+            LINES[index + 1] = val;
+            LINES[index + 2] = 80;
+            LINES[index + 3] = val;
             index += 4;
             val += 8 + (2.0f / NUM_PARALLEL_LINES);
         }
         val = 0;
         for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
-            gLinePts[index + 0] = val;
-            gLinePts[index + 1] = 150;
-            gLinePts[index + 2] = val;
-            gLinePts[index + 3] = 300;
+            LINES[index + 0] = val;
+            LINES[index + 1] = 40;
+            LINES[index + 2] = val;
+            LINES[index + 3] = 80;
             index += 4;
             val += 8 + (2.0f / NUM_PARALLEL_LINES);
         }
@@ -81,7 +78,7 @@
     // paint object, like anti-aliasing or drawing. Within those LinkedHashMaps are the various
     // options for that specific topic, which contains a displaymodifier which will affect the
     // given canvas and paint objects.
-    public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> sMaps =
+    public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> MAPS =
             new LinkedHashMap<String, LinkedHashMap<String,DisplayModifier>>() {
                 {
                     put("aa", new LinkedHashMap<String, DisplayModifier>() {
@@ -225,7 +222,7 @@
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
                                     canvas.rotate(90);
-                                    canvas.translate(0, -200);
+                                    canvas.translate(0, -100);
                                 }
                             });
                             put("scale2x2", new DisplayModifier() {
@@ -347,13 +344,13 @@
                             put("roundRect", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawRoundRect(gRect, 20, 20, paint);
+                                    canvas.drawRoundRect(RECT, 20, 20, paint);
                                 }
                             });
                             put("rect", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawRect(gRect, paint);
+                                    canvas.drawRect(RECT, paint);
                                 }
                             });
                             put("circle", new DisplayModifier() {
@@ -365,36 +362,32 @@
                             put("oval", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawOval(gRect, paint);
+                                    canvas.drawOval(RECT, paint);
                                 }
                             });
                             put("lines", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawLines(gLinePts, paint);
+                                    canvas.drawLines(LINES, paint);
                                 }
                             });
-                            /* drawPoints does not work with zero stroke width,
-                             * but it isn't a regression
-                             * TODO: fix hardware canvas so that drawPoints works
                             put("plusPoints", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawPoints(gPts, paint);
+                                    canvas.drawPoints(POINTS, paint);
                                 }
                             });
-                             */
                             put("text", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    paint.setTextSize(36);
+                                    paint.setTextSize(20);
                                     canvas.drawText("TEXTTEST", 0, 50, paint);
                                 }
                             });
                             put("shadowtext", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    paint.setTextSize(36);
+                                    paint.setTextSize(20);
                                     paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
                                     canvas.drawText("TEXTTEST", 0, 50, paint);
                                 }
@@ -410,13 +403,13 @@
                             put("arc", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawArc(gRect, 260, 285, false, paint);
+                                    canvas.drawArc(RECT, 260, 285, false, paint);
                                 }
                             });
                             put("arcFromCenter", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawArc(gRect, 260, 285, true, paint);
+                                    canvas.drawArc(RECT, 260, 285, true, paint);
                                 }
                             });
                         }
@@ -454,9 +447,9 @@
             // Create a Display Map of the valid indices
             mDisplayMap = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>();
             int index = 0;
-            for (String key : DisplayModifier.sMaps.keySet()) {
+            for (String key : DisplayModifier.MAPS.keySet()) {
                 if (validIndex(index)) {
-                    mDisplayMap.put(key, DisplayModifier.sMaps.get(key));
+                    mDisplayMap.put(key, DisplayModifier.MAPS.get(key));
                 }
                 index++;
             }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 166b6ff..4bd513a 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -17,11 +17,12 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
-import android.content.res.Configuration;
+import android.graphics.Point;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.view.View;
+import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.webkit.WebView;
 
@@ -31,26 +32,21 @@
  * A generic activity that uses a view specified by the user.
  */
 public class DrawActivity extends Activity {
-    private final static long TIME_OUT = 10000;
-    private final Object mLock = new Object();
+    private final static long TIME_OUT_MS = 10000;
+    private final Point mLock = new Point();
     public static final int MIN_NUMBER_OF_DRAWS = 20;
 
     private Handler mHandler;
     private View mView;
-    private boolean mOnWatch;
 
     public void onCreate(Bundle bundle){
         super.onCreate(bundle);
+        getWindow().getDecorView().setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
         mHandler = new RenderSpecHandler();
-        int uiMode = getResources().getConfiguration().uiMode;
-        mOnWatch = (uiMode & Configuration.UI_MODE_TYPE_WATCH) == Configuration.UI_MODE_TYPE_WATCH;
     }
 
-    public boolean getOnWatch() {
-        return mOnWatch;
-    }
-
-    public void enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, String webViewUrl,
+    public Point enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, String webViewUrl,
             @Nullable ViewInitializer viewInitializer, boolean useHardware) {
         ((RenderSpecHandler) mHandler).setViewInitializer(viewInitializer);
         int arg2 = (useHardware ? View.LAYER_TYPE_NONE : View.LAYER_TYPE_SOFTWARE);
@@ -62,13 +58,16 @@
             mHandler.obtainMessage(RenderSpecHandler.LAYOUT_MSG, layoutId, arg2).sendToTarget();
         }
 
+        Point point = new Point();
         synchronized (mLock) {
             try {
-                mLock.wait(TIME_OUT);
+                mLock.wait(TIME_OUT_MS);
+                point.set(mLock.x, mLock.y);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
+        return point;
     }
 
     private class RenderSpecHandler extends Handler {
@@ -83,39 +82,40 @@
         }
 
         public void handleMessage(Message message) {
-            int webViewBuffer = 0;
+            int drawCountDelay = 0;
+            setContentView(R.layout.test_container);
+            ViewStub stub = (ViewStub) findViewById(R.id.test_content_stub);
             switch (message.what) {
                 case LAYOUT_MSG: {
-                    setContentView(message.arg1);
-                    mView = findViewById(R.id.test_root);
-                    if (mView == null) {
-                        throw new IllegalStateException("test_root failed to inflate");
-                    }
+                    stub.setLayoutResource(message.arg1);
+                    mView = stub.inflate();
                 } break;
 
                 case CANVAS_MSG: {
-                    mView = new CanvasClientView(getApplicationContext(),
-                            (CanvasClient) (message.obj), ActivityTestBase.TEST_WIDTH,
-                            ActivityTestBase.TEST_HEIGHT);
-                    setContentView(mView);
+                    stub.setLayoutResource(R.layout.test_content_canvasclientview);
+                    mView = stub.inflate();
+                    ((CanvasClientView) mView).setCanvasClient((CanvasClient) (message.obj));
                 } break;
 
                 case WEB_VIEW_MSG: {
-                    mView = new WebView(getApplicationContext());
+                    stub.setLayoutResource(R.layout.test_content_webview);
+                    mView = stub.inflate();
                     ((WebView) mView).loadUrl((String) message.obj);
                     ((WebView) mView).setInitialScale(100);
-                    setContentView(mView);
-                    webViewBuffer = 10;
+                    drawCountDelay = 10;
                 } break;
             }
 
+            if (mView == null) {
+                throw new IllegalStateException("failed to inflate test content");
+            }
+
             if (mViewInitializer != null) {
                 mViewInitializer.intializeView(mView);
             }
-
             mView.setLayerType(message.arg2, null);
 
-            DrawCounterListener onDrawListener = new DrawCounterListener(webViewBuffer);
+            DrawCounterListener onDrawListener = new DrawCounterListener(drawCountDelay);
 
             mView.getViewTreeObserver().addOnPreDrawListener(onDrawListener);
 
@@ -133,11 +133,13 @@
 
         @Override
         public boolean onPreDraw() {
+
             mCurrentDraws++;
             if (mCurrentDraws < MIN_NUMBER_OF_DRAWS + mExtraDraws) {
                 mView.postInvalidate();
             } else {
                 synchronized (mLock) {
+                    mLock.set(mView.getLeft(), mView.getTop());
                     mLock.notify();
                 }
                 mView.getViewTreeObserver().removeOnPreDrawListener(this);
diff --git a/tests/tests/util/src/android/util/cts/EventLogTest.java b/tests/tests/util/src/android/util/cts/EventLogTest.java
index 12df64f..bbff3bc 100644
--- a/tests/tests/util/src/android/util/cts/EventLogTest.java
+++ b/tests/tests/util/src/android/util/cts/EventLogTest.java
@@ -69,8 +69,6 @@
         EventLog.writeEvent(ANSWER_TAG, 12345L, longString.toString());
         EventLog.writeEvent(ANSWER_TAG, longString.toString(), longString.toString());
         EventLog.writeEvent(ANSWER_TAG, longArray);
-        // Give the message some time to show up in the log
-        Thread.sleep(10);
         List<Event> events = getEventsAfterMarker(markerData, ANSWER_TAG);
         assertEquals(6, events.size());
 
@@ -156,8 +154,11 @@
     }
 
     /** Return elements after and the event that has the marker data and matching tag. */
-    private List<Event> getEventsAfterMarker(Object marker, int... tags) throws IOException {
+    private List<Event> getEventsAfterMarker(Object marker, int... tags)
+            throws IOException, InterruptedException {
         List<Event> events = new ArrayList<Event>();
+        // Give the message some time to show up in the log
+        Thread.sleep(20);
         EventLog.readEvents(tags, events);
 
         for (Iterator<Event> itr = events.iterator(); itr.hasNext(); ) {
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 6806d29..a36e6fd 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -84,7 +84,7 @@
         </activity>
         
         <activity android:name="android.view.animation.cts.AnimationTestCtsActivity"
-            android:label="AnimationTestCtsActivity">
+            android:label="AnimationTestCtsActivity" android:configChanges="orientation|screenSize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/tests/tests/view/res/anim-land/changing_reset_state_anim.xml b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
new file mode 100644
index 0000000..0b3279a
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_state_list_animator.xml b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
new file mode 100644
index 0000000..4c1c41e
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+    </item>
+    <!-- base state-->
+    <item>
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="200dp" android:valueType="floatType"/>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_test_animator.xml b/tests/tests/view/res/anim-land/changing_test_animator.xml
new file mode 100644
index 0000000..d5c83cd
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:propertyName="x" android:duration="100"
+                android:interpolator="@android:anim/bounce_interpolator"
+                android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_reset_state_anim.xml b/tests/tests/view/res/anim/changing_reset_state_anim.xml
new file mode 100644
index 0000000..dd2b233
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_state_list_animator.xml b/tests/tests/view/res/anim/changing_state_list_animator.xml
new file mode 100644
index 0000000..6bcc0d9
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+    </item>
+    <!-- base state-->
+    <item>
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_test_animator.xml b/tests/tests/view/res/anim/changing_test_animator.xml
new file mode 100644
index 0000000..739a71b
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:propertyName="x" android:duration="100"
+                android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+                android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/reset_state_anim.xml b/tests/tests/view/res/anim/reset_state_anim.xml
new file mode 100644
index 0000000..4bbbe62
--- /dev/null
+++ b/tests/tests/view/res/anim/reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_animator.xml b/tests/tests/view/res/anim/test_animator.xml
new file mode 100644
index 0000000..94e9ec8
--- /dev/null
+++ b/tests/tests/view/res/anim/test_animator.xml
@@ -0,0 +1,21 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="@dimen/test_animator_target_x" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="@dimen/test_animator_target_y" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="left" android:duration="100" android:valueTo="2" android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator.xml b/tests/tests/view/res/anim/test_state_list_animator.xml
new file mode 100644
index 0000000..b6a4822
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator.xml
@@ -0,0 +1,33 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <set>
+            <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+        </set>
+    </item>
+    <item android:state_enabled="true" android:state_pressed="false">
+        <set>
+            <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+        </set>
+    </item>
+    <!-- base state-->
+    <item android:animation="@anim/reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator_2.xml b/tests/tests/view/res/anim/test_state_list_animator_2.xml
new file mode 100644
index 0000000..6aeb41f
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator_2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+    </item>
+    <!-- base state-->
+    <item android:animation="@anim/changing_reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/values-land/dimens.xml b/tests/tests/view/res/values-land/dimens.xml
new file mode 100644
index 0000000..17b5395
--- /dev/null
+++ b/tests/tests/view/res/values-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.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.
+-->
+<resources>
+    <dimen name="test_animator_target_y">30dp</dimen>
+    <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced
+    in the XML on purpose-->
+    <dimen name="reset_state_value">100dp</dimen>
+    <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+    <dimen name="changing_state_list_anim_target_x_value">200dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/res/values/dimens.xml b/tests/tests/view/res/values/dimens.xml
new file mode 100644
index 0000000..16e5084
--- /dev/null
+++ b/tests/tests/view/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.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.
+-->
+<resources>
+    <dimen name="test_animator_target_x">10dp</dimen>
+    <dimen name="test_animator_target_y">20dp</dimen>
+    <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced in the XML on purpose-->
+    <dimen name="reset_state_value">50dp</dimen>
+    <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+    <dimen name="changing_state_list_anim_target_x_value">100dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java b/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java
index 48692f1..1ef9e48 100644
--- a/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java
+++ b/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java
@@ -20,11 +20,41 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.util.Log;
+
+import java.util.concurrent.TimeUnit;
 
 public class AnimationTestCtsActivity extends Activity {
+    final static long VISIBLE_TIMEOUT = TimeUnit.SECONDS.toNanos(3);
+    private boolean mIsVisible;
+
+    public boolean isVisible() {
+        return mIsVisible;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.anim_layout);
     }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mIsVisible = true;
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mIsVisible = false;
+    }
+
+    public boolean waitUntilVisible() throws InterruptedException {
+        long start = System.nanoTime();
+        while (!mIsVisible && (System.nanoTime() - start) < VISIBLE_TIMEOUT) {
+            Thread.sleep(100);
+        }
+        return mIsVisible;
+    }
 }
diff --git a/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
new file mode 100644
index 0000000..cc8ada0
--- /dev/null
+++ b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
@@ -0,0 +1,299 @@
+/*
+* 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.view.animation.cts;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.StateListAnimator;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.util.Log;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.Display;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.cts.view.R;
+
+public class AnimatorInflaterTest
+        extends ActivityInstrumentationTestCase2<AnimationTestCtsActivity> {
+
+    private static final String TAG = "AnimatorInflaterTest";
+    Set<Integer> identityHashes = new HashSet<Integer>();
+
+    public AnimatorInflaterTest() {
+        super("com.android.cts.view", AnimationTestCtsActivity.class);
+    }
+
+    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 testLoadAnimatorWithDifferentInterpolators() throws Throwable {
+        Animator anim1 = AnimatorInflater
+                .loadAnimator(getActivity(), R.anim.changing_test_animator);
+        if (!rotate()) {
+            return;//cancel test
+        }
+        Animator anim2 = AnimatorInflater
+                .loadAnimator(getActivity(), R.anim.changing_test_animator);
+        assertNotSame(anim1, anim2);
+        assertNotSame("interpolater is orientation dependent, should change",
+                anim1.getInterpolator(), anim2.getInterpolator());
+    }
+
+    /**
+     * Tests animators with dimension references.
+     */
+    public void testLoadAnimator() throws Throwable {
+        // to identify objects
+        Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        assertNotSame("a different animation should be returned", anim1, anim2);
+        assertSame("interpolator should be shallow cloned", anim1.getInterpolator(),
+                anim2.getInterpolator());
+        for (int i = 0; i < 2; i++) {
+            float targetX = getActivity().getResources()
+                    .getDimension(R.dimen.test_animator_target_x);
+            // y value changes in landscape orientation
+            float targetY = getActivity().getResources()
+                    .getDimension(R.dimen.test_animator_target_y);
+            for (Animator anim : new Animator[]{anim1, anim2}) {
+                assertTrue(anim instanceof AnimatorSet);
+                assertUnique(anim);
+                AnimatorSet set = (AnimatorSet) anim;
+                assertEquals("should have 3 sub animations", 3, set.getChildAnimations().size());
+                for (Animator subAnim : set.getChildAnimations()) {
+                    assertUnique(subAnim);
+                    assertTrue(subAnim instanceof ObjectAnimator);
+                }
+                final ObjectAnimator child1 = (ObjectAnimator) set.getChildAnimations().get(0);
+                final ObjectAnimator child2 = (ObjectAnimator) set.getChildAnimations().get(1);
+                final DummyObject dummyObject = new DummyObject();
+                runTestOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        for (ObjectAnimator animator : new ObjectAnimator[]{child1, child2}) {
+                            animator.setTarget(dummyObject);
+                            animator.setupStartValues();
+                            animator.start();
+                            animator.end();
+                        }
+                    }
+                });
+                assertEquals(targetX, dummyObject.x);
+                assertEquals(targetY, dummyObject.y);
+            }
+            if (i == 0) {
+                if (!rotate()) {
+                    return;//cancel test
+                }
+            }
+            anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+            anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+
+        }
+    }
+
+    private boolean rotate() throws Throwable {
+        WindowManager mWindowManager = (WindowManager) getActivity()
+                .getSystemService(Context.WINDOW_SERVICE);
+        Display display = mWindowManager.getDefaultDisplay();
+
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                getActivity().getClass().getName(), null, false);
+        getInstrumentation().addMonitor(monitor);
+        int nextRotation = 0;
+        switch (display.getRotation()) {
+            case Surface.ROTATION_0:
+            case Surface.ROTATION_180:
+                nextRotation = UiAutomation.ROTATION_FREEZE_90;
+                break;
+            case Surface.ROTATION_90:
+            case Surface.ROTATION_270:
+                nextRotation = UiAutomation.ROTATION_FREEZE_0;
+                break;
+            default:
+                Log.e(TAG, "Cannot get rotation, test is canceled");
+                return false;
+        }
+        boolean rotated = getInstrumentation().getUiAutomation().setRotation(nextRotation);
+        Thread.sleep(500);
+        if (!rotated) {
+            Log.e(TAG, "Rotation failed, test is canceled");
+        }
+        getInstrumentation().waitForIdleSync();
+        if (!getActivity().waitUntilVisible()) {
+            Log.e(TAG, "Activity failed to complete rotation, canceling test");
+            return false;
+        }
+        if (getActivity().getWindowManager().getDefaultDisplay().getRotation() != nextRotation) {
+            Log.e(TAG, "New activity orientation does not match. Canceling test");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Simple state list animator test that checks for cloning
+     */
+    public void testLoadStateListAnimator() {
+        StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.test_state_list_animator);
+        StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.test_state_list_animator);
+        assertUnique(sla1);
+        assertUnique(sla2);
+    }
+
+    /**
+     * Tests a state list animator which has an @anim reference that has different xmls per
+     * orientation
+     */
+    public void testLoadStateListAnimatorWithChangingResetState() throws Throwable {
+        loadStateListAnimatorWithChangingResetStateTest();
+        if (!rotate()) {
+            return;//cancel test
+        }
+
+        loadStateListAnimatorWithChangingResetStateTest();
+    }
+
+    private void loadStateListAnimatorWithChangingResetStateTest() throws Throwable {
+        final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.test_state_list_animator_2);
+        final View testView = getTestView();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testView.setStateListAnimator(sla);
+                testView.jumpDrawablesToCurrentState();
+            }
+        });
+        float resetValue = getActivity().getResources().getDimension(R.dimen.reset_state_value);
+        getInstrumentation().waitForIdleSync();
+        assertEquals(resetValue, testView.getX());
+        assertEquals(resetValue, testView.getY());
+        assertEquals(resetValue, testView.getZ());
+    }
+
+    /**
+     * Tests a state list animator which has different xml descriptions per orientation.
+     */
+    public void testLoadChangingStateListAnimator() throws Throwable {
+        loadChangingStateListAnimatorTest();
+        if (!rotate()) {
+            return;//cancel test
+        }
+        loadChangingStateListAnimatorTest();
+    }
+
+    private void loadChangingStateListAnimatorTest() throws Throwable {
+        final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.changing_state_list_animator);
+        final View testView = getTestView();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testView.setStateListAnimator(sla);
+                testView.jumpDrawablesToCurrentState();
+            }
+        });
+        float targetValue = getActivity().getResources()
+                .getDimension(R.dimen.changing_state_list_anim_target_x_value);
+        getInstrumentation().waitForIdleSync();
+        assertEquals(targetValue, testView.getX());
+    }
+
+    /**
+     * Tests that makes sure that reloaded animator is not affected by previous changes
+     */
+    public void testReloadedAnimatorIsNotModified() throws Throwable {
+        final Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        final CountDownLatch mStarted = new CountDownLatch(1);
+        final AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                mStarted.countDown();
+            }
+        };
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                anim1.setTarget(getTestView());
+                anim1.addListener(listener);
+                anim1.start();
+            }
+        });
+        Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        assertTrue(anim1.isStarted());
+        assertFalse(anim2.isStarted());
+        assertFalse("anim2 should not include the listener",
+                anim2.getListeners() != null && anim2.getListeners().contains(listener));
+        assertTrue("animator should start", mStarted.await(10, TimeUnit.SECONDS));
+        assertFalse(anim2.isRunning());
+
+    }
+
+    public View getTestView() {
+        return getActivity().findViewById(R.id.anim_window);
+    }
+
+    class DummyObject {
+
+        float x;
+        float y;
+
+        public float getX() {
+            return x;
+        }
+
+        public void setX(float x) {
+            this.x = x;
+        }
+
+        public float getY() {
+            return y;
+        }
+
+        public void setY(float y) {
+            this.y = y;
+        }
+    }
+}
+
diff --git a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
index 40d1d3d..6007730 100644
--- a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
@@ -19,7 +19,6 @@
 import com.android.cts.view.R;
 import com.android.internal.view.menu.MenuBuilder;
 
-
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
@@ -28,6 +27,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.SubMenu;
@@ -48,17 +48,22 @@
     protected void setUp() throws Exception {
         super.setUp();
         mActivity = getActivity();
-        mMenuInflater = mActivity.getMenuInflater();
     }
 
+    @UiThreadTest
     public void testConstructor() {
         new MenuInflater(mActivity);
     }
 
+    @UiThreadTest
     public void testInflate() {
         Menu menu = new MenuBuilder(mActivity);
         assertEquals(0, menu.size());
 
+        if (mMenuInflater == null) {
+            mMenuInflater = mActivity.getMenuInflater();
+        }
+
         mMenuInflater.inflate(com.android.cts.view.R.menu.browser, menu);
         assertNotNull(menu);
         assertEquals(1, menu.size());
@@ -77,7 +82,12 @@
     }
 
     // Check wheher the objects are created correctly from xml files
+    @UiThreadTest
     public void testInflateFromXml(){
+        if (mMenuInflater == null) {
+            mMenuInflater = mActivity.getMenuInflater();
+        }
+
         // the visibility and shortcut
         Menu menu = new MenuBuilder(mActivity);
         mMenuInflater.inflate(R.menu.visible_shortcut, menu);
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index e8da40e..ffbec1e 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -22,6 +22,7 @@
 import android.graphics.ColorFilter;
 import android.graphics.PorterDuff;
 
+import android.os.Bundle;
 import com.android.cts.view.R;
 import com.android.internal.view.menu.ContextMenuBuilder;
 import com.google.android.collect.Lists;
@@ -3667,6 +3668,11 @@
         public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
             return false;
         }
+
+        @Override
+        public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
+            return false;
+        }
     }
 
     private final class OnCreateContextMenuListenerImpl implements OnCreateContextMenuListener {
diff --git a/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java b/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
index fb44334..e8f0cab 100644
--- a/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
@@ -144,7 +144,7 @@
 
     public void testGuessFileName() {
         String url = "ftp://example.url/test";
-        assertEquals("test.jpeg", URLUtil.guessFileName(url, null, "image/jpeg"));
+        assertEquals("test.jpg", URLUtil.guessFileName(url, null, "image/jpeg"));
 
         assertEquals("test.bin", URLUtil.guessFileName(url, null, "application/octet-stream"));
     }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 33a9cee..e9ff48f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -95,7 +95,7 @@
      * brackets are optional):
      * <p/>
      * Mozilla/5.0 (Linux;[ U;] Android <version>;[ <language>-<country>;]
-     * [<devicemodel>;] Build/<buildID>) AppleWebKit/<major>.<minor> (KHTML, like Gecko)
+     * [<devicemodel>;] Build/<buildID>[; wv]) AppleWebKit/<major>.<minor> (KHTML, like Gecko)
      * Version/<major>.<minor> Chrome/<major>.<minor>.<branch>.<build>[ Mobile]
      * Safari/<major>.<minor>
      */
@@ -107,7 +107,8 @@
         Log.i(LOG_TAG, String.format("Checking user agent string %s", actualUserAgentString));
         final String patternString =
                 "Mozilla/5\\.0 \\(Linux;( U;)? Android ([^;]+);( (\\w+)-(\\w+);)?" +
-                "\\s?(.*)\\sBuild/(.+)\\) AppleWebKit/(\\d+)\\.(\\d+) \\(KHTML, like Gecko\\) " +
+                "\\s?(.*)\\sBuild/(.+?)(; wv)?\\) AppleWebKit/(\\d+)\\.(\\d+) " +
+                "\\(KHTML, like Gecko\\) " +
                 "Version/\\d+\\.\\d+ Chrome/\\d+\\.\\d+\\.\\d+\\.\\d+( Mobile)? " +
                 "Safari/(\\d+)\\.(\\d+)";
         // Groups used:
@@ -118,11 +119,12 @@
         //  5   - language
         //  6 - device model (optional)
         //  7 - build ID
-        //  8 - AppleWebKit major version number
-        //  9 - AppleWebKit minor version number
-        // 10 - " Mobile" string (optional)
-        // 11 - Safari major version number
-        // 12 - Safari minor version number
+        //  8 - WebView identifier "; wv" (optional)
+        //  9 - AppleWebKit major version number
+        // 10 - AppleWebKit minor version number
+        // 11 - " Mobile" string (optional)
+        // 12 - Safari major version number
+        // 13 - Safari minor version number
         Log.i(LOG_TAG, String.format("Trying to match pattern %s", patternString));
         final Pattern userAgentExpr = Pattern.compile(patternString);
         Matcher patternMatcher = userAgentExpr.matcher(actualUserAgentString);
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index b381d72..6572285 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -1456,14 +1456,10 @@
                 "Find all instances of a word on the page and highlight them.</p>";
 
         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null);
-        WaitForFindResultsListener l = new WaitForFindResultsListener();
-        mOnUiThread.setFindListener(l);
 
         // highlight all the strings found
         mOnUiThread.findAll("all");
-        // make sure the findAll action is completed before findNext
-        l.get();
-        mOnUiThread.setFindListener(null);
+        getInstrumentation().waitForIdleSync();
 
         int previousScrollY = mOnUiThread.getScrollY();
 
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 81a1a4b..6a2240e 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -37,6 +37,7 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.AdapterView.OnItemSelectedListener;
+import android.provider.Settings;
 
 import com.android.cts.widget.R;
 
@@ -211,8 +212,12 @@
         setArrayAdapter(mAdapterView);
 
         // LastVisiblePosition should be adapter's getCount - 1,by mocking method
+        float fontScale = Settings.System.getFloat(mActivity.getContentResolver(), Settings.System.FONT_SCALE, 1);
+        if (fontScale < 1) {
+            fontScale = 1;
+        }
         float density = mActivity.getResources().getDisplayMetrics().density;
-        int bottom = (int) (LAYOUT_HEIGHT * density);
+        int bottom = (int) (LAYOUT_HEIGHT * density * fontScale);
         mAdapterView.layout(0, 0, LAYOUT_WIDTH, bottom);
         assertEquals(FRUIT.length - 1, mAdapterView.getLastVisiblePosition());
     }
diff --git a/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java b/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java
index da99fa3..c08abbc 100644
--- a/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java
@@ -502,6 +502,7 @@
                 }
             }.run();
         } else {
+            Thread.sleep(200);
             mInstrumentation.sendStringSync(STRING_TEST);
             new PollingCheck() {
                 @Override
diff --git a/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java b/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
index 41018a9..9589fec 100644
--- a/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
@@ -30,28 +30,10 @@
  * Stub activity for testing {@link PopupWindow}
  */
 public class MockPopupWindowCtsActivity extends Activity {
-    private boolean isFirstRun = true;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Window window = getWindow();
-        final View decor = window.getDecorView();
-        decor.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener() {
-            @Override
-            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
-                if (isFirstRun) {
-                    if (insets.isRound()) {
-                        decor.setPadding(decor.getPaddingLeft(), decor.getPaddingTop(),
-                                decor.getPaddingRight(),
-                                decor.getPaddingBottom() + insets.getSystemWindowInsetBottom());
-                    }
-                    isFirstRun = false;
-                    setContentView(R.layout.popupwindow);
-                }
-                return insets.consumeSystemWindowInsets();
-            }
-        });
+        setContentView(R.layout.popupwindow);
     }
 }
 
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index e1742c8..ae12f9c 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -273,18 +273,21 @@
     }
 
     public void testShowAtLocation() {
-        int[] viewInWindowXY = new int[2];
-        int[] viewOnScreenXY = new int[2];
+        int[] popupContentViewInWindowXY = new int[2];
+        int[] popupContentViewOnScreenXY = new int[2];
 
         mPopupWindow = createPopupWindow(createPopupContent());
+        // Do not attach within the decor; we will be measuring location
+        // with regard to screen coordinates.
+        mPopupWindow.setAttachedInDecor(false);
         final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
 
         final int xOff = 10;
         final int yOff = 21;
         assertFalse(mPopupWindow.isShowing());
-        mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
-        assertEquals(0, viewInWindowXY[0]);
-        assertEquals(0, viewInWindowXY[1]);
+        mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY);
+        assertEquals(0, popupContentViewInWindowXY[0]);
+        assertEquals(0, popupContentViewInWindowXY[1]);
 
         mInstrumentation.runOnMainSync(new Runnable() {
             public void run() {
@@ -294,12 +297,12 @@
         mInstrumentation.waitForIdleSync();
 
         assertTrue(mPopupWindow.isShowing());
-        mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
-        mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY);
-        assertTrue(viewInWindowXY[0] >= 0);
-        assertTrue(viewInWindowXY[1] >= 0);
-        assertEquals(viewInWindowXY[0] + xOff, viewOnScreenXY[0]);
-        assertEquals(viewInWindowXY[1] + yOff, viewOnScreenXY[1]);
+        mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY);
+        mPopupWindow.getContentView().getLocationOnScreen(popupContentViewOnScreenXY);
+        assertTrue(popupContentViewInWindowXY[0] >= 0);
+        assertTrue(popupContentViewInWindowXY[1] >= 0);
+        assertEquals(popupContentViewInWindowXY[0] + xOff, popupContentViewOnScreenXY[0]);
+        assertEquals(popupContentViewInWindowXY[1] + yOff, popupContentViewOnScreenXY[1]);
 
         dismissPopup();
     }
@@ -355,7 +358,11 @@
         assertTrue(maxAvailableHeightWithOffset <= avaliable);
 
         anchorView = mActivity.findViewById(R.id.anchor_lower);
-        avaliable = getDisplay().getHeight() - anchorView.getHeight();
+        // On some devices the view might actually have larger size than the physical display
+        // due to chin and content will be laid out as if outside of the display. We need to use
+        // larger from the display height and the main view height.
+        avaliable = Math.max(getDisplay().getHeight(),
+                mActivity.findViewById(android.R.id.content).getHeight()) - anchorView.getHeight();
         maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView);
         assertTrue(maxAvailableHeight > 0);
         assertTrue(maxAvailableHeight <= avaliable);
@@ -453,6 +460,9 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             public void run() {
                 mPopupWindow = createPopupWindow(createPopupContent());
+                // Do not attach within the decor; we will be measuring location
+                // with regard to screen coordinates.
+                mPopupWindow.setAttachedInDecor(false);
             }
         });
 
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
index c8211f6..5259736 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
@@ -404,8 +404,8 @@
         // exceptional
         try {
             tableLayout.addView(null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
+            fail("Should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
         }
     }
 
@@ -445,8 +445,8 @@
 
         try {
             tableLayout.addView(null, -1);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
+            fail("Should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
         }
     }
 
@@ -477,8 +477,8 @@
 
         try {
             tableLayout.addView(null, new TableLayout.LayoutParams(200, 300));
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
+            fail("Should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
         }
 
         try {
@@ -540,8 +540,8 @@
 
         try {
             tableLayout.addView(null, -1, new TableLayout.LayoutParams(200, 300));
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
+            fail("Should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
         }
 
         try {
diff --git a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
index 7e44e49..7e4c367 100644
--- a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
+++ b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
@@ -827,17 +827,17 @@
         assertTrue("Pinch must be in center of target view", p2s.y == screenRect.centerY());
 
         assertTrue("Touch-down X coordinate for pointer 1 is invalid",
-                withinMarginOfError(0.1f, screenRect.centerX(), p1s.x));
+                withinMarginOfError(0.125f, screenRect.centerX(), p1s.x));
 
         assertTrue("Touch-down X coordinate for pointer 2 is invalid",
-                withinMarginOfError(0.1f, screenRect.centerX(), p2s.x));
+                withinMarginOfError(0.125f, screenRect.centerX(), p2s.x));
 
         assertTrue("Touch-up X coordinate for pointer 1 is invalid",
-                withinMarginOfError(0.1f, screenRect.centerX() - screenRect.left,
+                withinMarginOfError(0.125f, screenRect.centerX() - screenRect.left,
                         screenRect.centerX() - p1e.x));
 
         assertTrue("Touch-up X coordinate for pointer 2 is invalid",
-                withinMarginOfError(0.1f, screenRect.right, p2e.x));
+                withinMarginOfError(0.125f, screenRect.right, p2e.x));
     }
 
     /**
@@ -881,17 +881,17 @@
         assertTrue("Pinch must be in center of target view", p2s.y == screenRect.centerY());
 
         assertTrue("Touch-down X coordinate for pointer 1 is invalid",
-                withinMarginOfError(0.1f, screenRect.centerX() - screenRect.left,
+                withinMarginOfError(0.125f, screenRect.centerX() - screenRect.left,
                         screenRect.centerX() -  p1s.x));
 
         assertTrue("Touch-down X coordinate for pointer 2 is invalid",
-                withinMarginOfError(0.1f, screenRect.right, p2s.x));
+                withinMarginOfError(0.125f, screenRect.right, p2s.x));
 
         assertTrue("Touch-up X coordinate for pointer 1 is invalid",
-                withinMarginOfError(0.1f, screenRect.centerX() - FINGER_TOUCH_HALF_WIDTH, p1e.x));
+                withinMarginOfError(0.125f, screenRect.centerX() - FINGER_TOUCH_HALF_WIDTH, p1e.x));
 
         assertTrue("Touch-up X coordinate for pointer 2 is invalid",
-                withinMarginOfError(0.1f, screenRect.centerX() + FINGER_TOUCH_HALF_WIDTH, p2e.x));
+                withinMarginOfError(0.125f, screenRect.centerX() + FINGER_TOUCH_HALF_WIDTH, p2e.x));
     }
 
     /**
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
index 62c268d..8fb61bf 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
@@ -46,6 +46,7 @@
     public static final String SCREEN_SIZE = "screen_size";
     public static final String SCREEN_DENSITY_BUCKET = "screen_density_bucket";
     public static final String SCREEN_DENSITY = "screen_density";
+    public static final String SMALLEST_SCREEN_WIDTH_DP = "smallest_screen_width_dp";
     public static final String RESOLUTION = "resolution";
     public static final String VERSION_SDK = "androidPlatformVersion";
     public static final String VERSION_RELEASE = "buildVersion";
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
index 52ddfe9..5828259 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
@@ -97,6 +97,9 @@
         String screenSize = getScreenSize();
         addResult(SCREEN_SIZE, screenSize);
 
+        Configuration configuration = getContext().getResources().getConfiguration();
+        addResult(SMALLEST_SCREEN_WIDTH_DP, configuration.smallestScreenWidthDp);
+
         Intent intent = new Intent();
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setClass(this.getContext(), DeviceInfoActivity.class);
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
index 932014a..0bf766d 100644
--- a/tools/selinux/SELinuxNeverallowTestFrame.py
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -72,8 +72,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);
     }
 """
 src_body = ""
diff --git a/tools/selinux/src/SELinux_CTS.py b/tools/selinux/src/SELinux_CTS.py
deleted file mode 100644
index ec12be0..0000000
--- a/tools/selinux/src/SELinux_CTS.py
+++ /dev/null
@@ -1,542 +0,0 @@
-import pdb
-import re
-from xml.etree.ElementTree import Element, SubElement, tostring
-
-#define equivalents
-TYPE = 0
-ATTRIBUTE = 1
-TYPEATTRIBUTE = 2
-CLASS = 3
-COMMON = 4
-ALLOW_RULE = 5
-NEVERALLOW_RULE = 6
-OTHER = 7
-
-#define helper methods
-# advance_past_whitespace(): helper function to skip whitespace at current
-# position in file.
-# returns: the non-whitespace character at the file's new position
-#TODO: should I deal with comments here as well?
-def advance_past_whitespace(file_obj):
-    c = file_obj.read(1)
-    while c.isspace():
-        c = file_obj.read(1)
-    file_obj.seek(-1, 1)
-    return c
-
-# advance_until_whitespace(): helper function to grab the string represented
-# by the current position in file until next whitespace.
-# returns: string until next whitespace.  overlooks comments.
-def advance_until_whitespace(file_obj):
-    ret_string = ""
-    c = file_obj.read(1)
-    #TODO: make a better way to deal with ':' and ';'
-    while not (c.isspace() or c == ':' or c == '' or c == ';'):
-        #don't count comments
-        if c == '#':
-            file_obj.readline()
-            return ret_string
-        else:
-            ret_string+=c
-            c = file_obj.read(1)
-    if not c == ':':
-        file_obj.seek(-1, 1)
-    return ret_string
-
-# expand_avc_rule - takes a processed avc rule and converts it into a list of
-# 4-tuples for use in an access check of form:
-    # (source_type, target_type, class, permission)
-def expand_avc_rule(policy, avc_rule):
-    ret_list = [ ]
-
-    #expand source_types
-    source_types = avc_rule['source_types']['set']
-    source_types = policy.expand_types(source_types)
-    if(avc_rule['source_types']['flags']['complement']):
-        #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
-        source_types = policy.types - source_types #complement these types
-    if len(source_types) == 0:
-        print "ERROR: source_types empty after expansion"
-        print "Before: "
-        print avc_rule['source_types']['set']
-        return
-
-    #expand target_types
-    target_types = avc_rule['target_types']['set']
-    target_types = policy.expand_types(target_types)
-    if(avc_rule['target_types']['flags']['complement']):
-        #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
-        target_types = policy.types - target_types #complement these types
-    if len(target_types) == 0:
-        print "ERROR: target_types empty after expansion"
-        print "Before: "
-        print avc_rule['target_types']['set']
-        return
-
-    # get classes
-    rule_classes = avc_rule['classes']['set']
-    if '' in rule_classes:
-        print "FOUND EMPTY STRING IN CLASSES"
-        print "Total sets:"
-        print avc_rule['source_types']['set']
-        print avc_rule['target_types']['set']
-        print rule_classes
-        print avc_rule['permissions']['set']
-
-    if len(rule_classes) == 0:
-        print "ERROR: empy set of object classes in avc rule"
-        return
-
-    # get permissions
-    permissions = avc_rule['permissions']['set']
-    if len(permissions) == 0:
-        print "ERROR: empy set of permissions in avc rule\n"
-        return
-
-    #create the list with collosal nesting, n^4 baby!
-    for s in source_types:
-        for t in target_types:
-            for c in rule_classes:
-                if c == '':
-                   continue
-                #expand permissions on a per-class basis
-                exp_permissions = policy.expand_permissions(c, permissions)
-                if(avc_rule['permissions']['flags']['complement']):
-                    exp_permissions = policy.classes[c] - exp_permissions
-                if len(exp_permissions) == 0:
-                    print "ERROR: permissions empty after expansion\n"
-                    print "Before: "
-                    print avc_rule['permissions']['set']
-                    return
-                for p in exp_permissions:
-                    source = s
-                    if t == 'self':
-                        target = s
-                    else:
-                        target = t
-                    obj_class = c
-                    permission = p
-                    ret_list.append((source, target, obj_class, permission))
-    return ret_list
-
-# expand_avc_rule - takes a processed avc rule and converts it into an xml
-# representation with the information needed in a checkSELinuxAccess() call.
-# (source_type, target_type, class, permission)
-def expand_avc_rule_to_xml(policy, avc_rule, rule_name, rule_type):
-    rule_xml = Element('avc_rule')
-    rule_xml.set('name', rule_name)
-    rule_xml.set('type', rule_type)
-
-    #expand source_types
-    source_types = avc_rule['source_types']['set']
-    source_types = policy.expand_types(source_types)
-    if(avc_rule['source_types']['flags']['complement']):
-        #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
-        source_types = policy.types - source_types #complement these types
-    if len(source_types) == 0:
-        print "ERROR: source_types empty after expansion"
-        print "Before: "
-        print avc_rule['source_types']['set']
-        return
-    for s in source_types:
-        elem = SubElement(rule_xml, 'type')
-        elem.set('type', 'source')
-        elem.text = s
-
-    #expand target_types
-    target_types = avc_rule['target_types']['set']
-    target_types = policy.expand_types(target_types)
-    if(avc_rule['target_types']['flags']['complement']):
-        #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
-        target_types = policy.types - target_types #complement these types
-    if len(target_types) == 0:
-        print "ERROR: target_types empty after expansion"
-        print "Before: "
-        print avc_rule['target_types']['set']
-        return
-    for t in target_types:
-        elem = SubElement(rule_xml, 'type')
-        elem.set('type', 'target')
-        elem.text = t
-
-    # get classes
-    rule_classes = avc_rule['classes']['set']
-
-    if len(rule_classes) == 0:
-        print "ERROR: empy set of object classes in avc rule"
-        return
-
-    # get permissions
-    permissions = avc_rule['permissions']['set']
-    if len(permissions) == 0:
-        print "ERROR: empy set of permissions in avc rule\n"
-        return
-
-    # permissions are class-dependent, so bundled together
-    for c in rule_classes:
-        if c == '':
-            print "AH!!! empty class found!\n"
-            continue
-        c_elem = SubElement(rule_xml, 'obj_class')
-        c_elem.set('name', c)
-        #expand permissions on a per-class basis
-        exp_permissions = policy.expand_permissions(c, permissions)
-        if(avc_rule['permissions']['flags']['complement']):
-            exp_permissions = policy.classes[c] - exp_permissions
-        if len(exp_permissions) == 0:
-            print "ERROR: permissions empty after expansion\n"
-            print "Before: "
-            print avc_rule['permissions']['set']
-            return
-
-        for p in exp_permissions:
-            p_elem = SubElement(c_elem, 'permission')
-            p_elem.text = p
-
-    return rule_xml
-
-# expand_brackets - helper function which reads a file into a string until '{ }'s
-# are balanced.  Brackets are removed from the string.  This function is based
-# on the understanding that nested brackets in our policy.conf file occur only due
-# to macro expansion, and we just need to know how much is included in a given
-# policy sub-component.
-def expand_brackets(file_obj):
-    ret_string = ""
-    c = file_obj.read(1)
-    if not c == '{':
-        print "Invalid bracket expression: " + c + "\n"
-        file_obj.seek(-1, 1)
-        return ""
-    else:
-        bracket_count = 1
-    while bracket_count > 0:
-        c = file_obj.read(1)
-        if c == '{':
-            bracket_count+=1
-        elif c == '}':
-            bracket_count-=1
-        elif c == '#':
-            #get rid of comment and replace with whitespace
-            file_obj.readline()
-            ret_string+=' '
-        else:
-            ret_string+=c
-    return ret_string
-
-# get_avc_rule_component - grabs the next component from an avc rule.  Basically,
-# just reads the next word or bracketed set of words.
-# returns - a set of the word, or words with metadata
-def get_avc_rule_component(file_obj):
-    ret_dict = { 'flags': {}, 'set': set() }
-    c = advance_past_whitespace(file_obj)
-    if c == '~':
-        ret_dict['flags']['complement'] = True
-        file_obj.read(1) #move to next char
-        c = advance_past_whitespace(file_obj)
-    else:
-        ret_dict['flags']['complement'] = False
-    if not c == '{':
-        #TODO: change operations on file to operations on string?
-        single_type =  advance_until_whitespace(file_obj)
-        ret_dict['set'].add(single_type)
-    else:
-        mult_types = expand_brackets(file_obj)
-        mult_types = mult_types.split()
-        for t in mult_types:
-            ret_dict['set'].add(t)
-    return ret_dict
-
-def get_line_type(line):
-    if re.search(r'^type\s', line):
-        return TYPE
-    if re.search(r'^attribute\s', line):
-        return ATTRIBUTE
-    if re.search(r'^typeattribute\s', line):
-        return TYPEATTRIBUTE
-    if re.search(r'^class\s', line):
-        return CLASS
-    if re.search(r'^common\s', line):
-        return COMMON
-    if re.search(r'^allow\s', line):
-        return ALLOW_RULE
-    if re.search(r'^neverallow\s', line):
-        return NEVERALLOW_RULE
-    else:
-        return OTHER
-
-def is_multi_line(line_type):
-    if line_type == CLASS:
-        return True
-    elif line_type == COMMON:
-        return True
-    elif line_type == ALLOW_RULE:
-        return True
-    elif line_type == NEVERALLOW_RULE:
-        return True
-    else:
-        return False
-
-
-#should only be called with file pointing to the 'i' in 'inherits' segment
-def process_inherits_segment(file_obj):
-    inherit_keyword = file_obj.read(8)
-    if not inherit_keyword == 'inherits':
-        #TODO: handle error, invalid class statement
-        print "ERROR: invalid inherits statement"
-        return
-    else:
-        advance_past_whitespace(file_obj)
-        ret_inherited_common = advance_until_whitespace(file_obj)
-        return ret_inherited_common
-
-class SELinuxPolicy:
-
-    def __init__(self):
-        self.types = set()
-        self.attributes = { }
-        self.classes = { }
-        self.common_classes = { }
-        self.allow_rules = [ ]
-        self.neverallow_rules = [ ]
-
-    # create policy directly from policy file
-    #@classmethod
-    def from_file_name(self, policy_file_name):
-        self.types = set()
-        self.attributes = { }
-        self.classes = { }
-        self.common_classes = { }
-        self.allow_rules = [ ]
-        self.neverallow_rules = [ ]
-        with open(policy_file_name, 'r') as policy_file:
-            line = policy_file.readline()
-            while line:
-                line_type = get_line_type(line)
-                if is_multi_line(line_type):
-                    self.parse_multi_line(line, line_type, policy_file)
-                else:
-                    self.parse_single_line(line, line_type)
-                line = policy_file.readline()
-
-    # expand_permissions - generates the actual permission set based on the listed
-    # permissions with wildcards and the given class on which they're based.
-    def expand_permissions(self, obj_class, permission_set):
-        ret_set = set()
-        neg_set = set()
-        for p in permission_set:
-            if p[0] == '-':
-                real_p = p[1:]
-                if real_p in self.classes[obj_class]:
-                    neg_set.add(real_p)
-                else:
-                    print "ERROR: invalid permission in avc rule " + real_t + "\n"
-                    return
-            else:
-                if p in self.classes[obj_class]:
-                    ret_set.add(p)
-                elif p == '*':  #pretty sure this can't be negated? eg -*
-                    ret_set |= self.classes[obj_class]  #All of the permissions
-                else:
-                    print "ERROR: invalid permission in avc rule " + p + "\n"
-                    return
-        return ret_set - neg_set
-
-    # expand_types - generates the actual type set based on the listed types,
-    # attributes, wildcards and negation.  self is left as-is, and is processed
-    # specially when generating checkAccess() 4-tuples
-    def expand_types(self, type_set):
-        ret_set = set()
-        neg_set = set()
-        for t in type_set:
-            if t[0] == '-':
-                real_t = t[1:]
-                if real_t in self.attributes:
-                    neg_set |= self.attributes[real_t]
-                elif real_t in self.types:
-                    neg_set.add(real_t)
-                elif real_t == 'self':
-                    ret_set |= real_t
-                else:
-                    print "ERROR: invalid type in avc rule " + real_t + "\nTYPE SET:"
-                    print type_set
-                    return
-            else:
-                if t in self.attributes:
-                     ret_set |= self.attributes[t]
-                elif t in self.types:
-                    ret_set.add(t)
-                elif t == 'self':
-                    ret_set.add(t)
-                elif t == '*':  #pretty sure this can't be negated?
-                     ret_set |= self.types  #All of the types
-                else:
-                    print "ERROR: invalid type in avc rule " + t + "\nTYPE SET"
-                    print type_set
-                    return
-        return ret_set - neg_set
-
-    def parse_multi_line(self, line, line_type, file_obj):
-        if line_type == CLASS:
-            self.process_class_line(line, file_obj)
-        elif line_type == COMMON:
-            self.process_common_line(line, file_obj)
-        elif line_type == ALLOW_RULE:
-            self.process_avc_rule_line(line, file_obj)
-        elif line_type == NEVERALLOW_RULE:
-            self.process_avc_rule_line(line, file_obj)
-        else:
-            print "Error: This is not a multi-line input"
-
-    def parse_single_line(self, line, line_type):
-        if line_type == TYPE:
-            self.process_type_line(line)
-        elif line_type == ATTRIBUTE:
-            self.process_attribute_line(line)
-        elif line_type == TYPEATTRIBUTE:
-            self.process_typeattribute_line(line)
-        return
-
-    def process_attribute_line(self, line):
-        match = re.search(r'^attribute\s+(.+);', line)
-        if match:
-            declared_attribute = match.group(1)
-            self.attributes[declared_attribute] = set()
-        else:
-            #TODO: handle error? (no state changed)
-            return
-
-    def process_class_line(self, line, file_obj):
-        match = re.search(r'^class\s([^\s]+)\s(.*$)', line)
-        if match:
-            declared_class = match.group(1)
-            #first class declaration has no perms
-            if not declared_class in self.classes:
-                self.classes[declared_class] = set()
-                return
-            else:
-                #need to parse file from after class name until end of '{ }'s
-                file_obj.seek(-(len(match.group(2)) + 1), 1)
-                c = advance_past_whitespace(file_obj)
-                if not (c == 'i' or c == '{'):
-                    print "ERROR: invalid class statement"
-                    return
-                elif c == 'i':
-                    #add inherited permissions
-                    inherited = process_inherits_segment(file_obj)
-                    self.classes[declared_class] |= self.common_classes[inherited]
-                    c = advance_past_whitespace(file_obj)
-                if c == '{':
-                    permissions = expand_brackets(file_obj)
-                    permissions = re.sub(r'#[^\n]*\n','\n' , permissions) #get rid of all comments
-                    permissions = permissions.split()
-                    for p in permissions:
-                        self.classes[declared_class].add(p)
-
-    def process_common_line(self, line, file_obj):
-        match = re.search(r'^common\s([^\s]+)(.*$)', line)
-        if match:
-            declared_common_class = match.group(1)
-            #TODO: common classes should only be declared once...
-            if not declared_common_class in self.common_classes:
-                self.common_classes[declared_common_class] = set()
-            #need to parse file from after common_class name until end of '{ }'s
-            file_obj.seek(-(len(match.group(2)) + 1), 1)
-            c = advance_past_whitespace(file_obj)
-            if not c == '{':
-                print "ERROR: invalid common statement"
-                return
-            permissions = expand_brackets(file_obj)
-            permissions = permissions.split()
-            for p in permissions:
-                self.common_classes[declared_common_class].add(p)
-        return
-
-    def process_avc_rule_line(self, line, file_obj):
-        match = re.search(r'^(never)?allow\s(.*$)', line)
-        if match:
-            if(match.group(1)):
-                rule_type = 'neverallow'
-            else:
-                rule_type = 'allow'
-            #need to parse file from after class name until end of '{ }'s
-            file_obj.seek(-(len(match.group(2)) + 1), 1)
-
-            #grab source type(s)
-            source_types = get_avc_rule_component(file_obj)
-            if len(source_types['set']) == 0:
-                print "ERROR: no source types for avc rule at line: " + line
-                return
-
-            #grab target type(s)
-            target_types = get_avc_rule_component(file_obj)
-            if len(target_types['set']) == 0:
-                print "ERROR: no target types for avc rule at line: " + line
-                return
-
-            #skip ':' potentially already handled by advance_until_whitespace
-            c = advance_past_whitespace(file_obj)
-            if c == ':':
-                file_obj.read(1)
-
-            #grab class(es)
-            classes = get_avc_rule_component(file_obj)
-            if len(classes['set']) == 0:
-                print "ERROR: no classes for avc rule at line: " + line
-                return
-
-            #grab permission(s)
-            permissions = get_avc_rule_component(file_obj)
-            if len(permissions['set']) == 0:
-                print "ERROR: no permissions for avc rule at line: " + line
-                return
-            rule_dict = {
-                'source_types': source_types,
-                'target_types': target_types,
-                'classes': classes,
-                'permissions': permissions }
-
-            if rule_type == 'allow':
-                self.allow_rules.append(rule_dict)
-            elif rule_type == 'neverallow':
-                self.neverallow_rules.append(rule_dict)
-
-    def process_type_line(self, line):
-        #TODO: add support for aliases (not yet in current policy.conf)
-        match = re.search(r'^type\s([^,]+),?(.*);', line)
-        if match:
-            declared_type = match.group(1)
-            self.types.add(declared_type)
-            if match.group(2):
-                declared_attributes = match.group(2)
-                declared_attributes = declared_attributes.replace(" ", "") #remove whitespace
-                declared_attributes = declared_attributes.split(',') #separate based on delimiter
-                for a in declared_attributes:
-                    if not a in self.attributes:
-                        #TODO: hanlde error? attribute should already exist
-                        self.attributes[a] = set()
-                    self.attributes[a].add(declared_type)
-        else:
-            #TODO: handle error? (no state changed)
-            return
-
-    def process_typeattribute_line(self, line):
-        match = re.search(r'^typeattribute\s([^\s]+)\s(.*);', line)
-        if match:
-            declared_type = match.group(1)
-            if not declared_type in self.types:
-                #TODO: handle error? type should already exist
-                self.types.add(declared_type)
-            if match.group(2):
-                declared_attributes = match.group(2)
-                declared_attributes = declared_attributes.replace(" ", "") #remove whitespace
-                declared_attributes = declared_attributes.split(',') #separate based on delimiter
-                for a in declared_attributes:
-                    if not a in self.attributes:
-                        #TODO: hanlde error? attribute should already exist
-                        self.attributes[a] = set()
-                    self.attributes[a].add(declared_type)
-            else:
-                return
-        else:
-            #TODO: handle error? (no state changed)
-            return
diff --git a/tools/selinux/src/example_input_policy.conf b/tools/selinux/src/example_input_policy.conf
deleted file mode 100644
index aeef5f8..0000000
--- a/tools/selinux/src/example_input_policy.conf
+++ /dev/null
@@ -1,9850 +0,0 @@
-#line 1 "external/sepolicy/security_classes"
-# FLASK
-
-#
-# Define the security object classes
-#
-
-# Classes marked as userspace are classes
-# for userspace object managers
-
-class security
-class process
-class system
-class capability
-
-# file-related classes
-class filesystem
-class file
-class dir
-class fd
-class lnk_file
-class chr_file
-class blk_file
-class sock_file
-class fifo_file
-
-# network-related classes
-class socket
-class tcp_socket
-class udp_socket
-class rawip_socket
-class node
-class netif
-class netlink_socket
-class packet_socket
-class key_socket
-class unix_stream_socket
-class unix_dgram_socket
-
-# sysv-ipc-related classes
-class sem
-class msg
-class msgq
-class shm
-class ipc
-
-#
-# userspace object manager classes
-#
-
-# passwd/chfn/chsh
-class passwd			# userspace
-
-# SE-X Windows stuff (more classes below)
-class x_drawable		# userspace
-class x_screen			# userspace
-class x_gc			# userspace
-class x_font			# userspace
-class x_colormap		# userspace
-class x_property		# userspace
-class x_selection		# userspace
-class x_cursor			# userspace
-class x_client			# userspace
-class x_device			# userspace
-class x_server			# userspace
-class x_extension		# userspace
-
-# extended netlink sockets
-class netlink_route_socket
-class netlink_firewall_socket
-class netlink_tcpdiag_socket
-class netlink_nflog_socket
-class netlink_xfrm_socket
-class netlink_selinux_socket
-class netlink_audit_socket
-class netlink_ip6fw_socket
-class netlink_dnrt_socket
-
-class dbus			# userspace
-class nscd			# userspace
-
-# IPSec association
-class association
-
-# Updated Netlink class for KOBJECT_UEVENT family.
-class netlink_kobject_uevent_socket
-
-class appletalk_socket
-
-class packet
-
-# Kernel access key retention
-class key
-
-class context			# userspace
-
-class dccp_socket
-
-class memprotect
-
-class db_database		# userspace
-class db_table			# userspace
-class db_procedure		# userspace
-class db_column			# userspace
-class db_tuple			# userspace
-class db_blob			# userspace
-
-# network peer labels
-class peer
-
-# Capabilities >= 32
-class capability2
-
-# More SE-X Windows stuff
-class x_resource		# userspace
-class x_event			# userspace
-class x_synthetic_event		# userspace
-class x_application_data	# userspace
-
-# kernel services that need to override task security, e.g. cachefiles
-class kernel_service
-
-class tun_socket
-
-# Still More SE-X Windows stuff
-class x_pointer			# userspace
-class x_keyboard		# userspace
-
-# More Database stuff
-class db_schema			# userspace
-class db_view			# userspace
-class db_sequence		# userspace
-class db_language		# userspace
-
-class binder
-class zygote
-
-# Property service
-class property_service          # userspace
-
-# FLASK
-#line 1 "external/sepolicy/initial_sids"
-# FLASK
-
-#
-# Define initial security identifiers
-#
-
-sid kernel
-sid security
-sid unlabeled
-sid fs
-sid file
-sid file_labels
-sid init
-sid any_socket
-sid port
-sid netif
-sid netmsg
-sid node
-sid igmp_packet
-sid icmp_socket
-sid tcp_socket
-sid sysctl_modprobe
-sid sysctl
-sid sysctl_fs
-sid sysctl_kernel
-sid sysctl_net
-sid sysctl_net_unix
-sid sysctl_vm
-sid sysctl_dev
-sid kmod
-sid policy
-sid scmp_packet
-sid devnull
-
-# FLASK
-#line 1 "external/sepolicy/access_vectors"
-#
-# Define common prefixes for access vectors
-#
-# common common_name { permission_name ... }
-
-
-#
-# Define a common prefix for file access vectors.
-#
-
-common file
-{
-	ioctl
-	read
-	write
-	create
-	getattr
-	setattr
-	lock
-	relabelfrom
-	relabelto
-	append
-	unlink
-	link
-	rename
-	execute
-	swapon
-	quotaon
-	mounton
-}
-
-
-#
-# Define a common prefix for socket access vectors.
-#
-
-common socket
-{
-# inherited from file
-	ioctl
-	read
-	write
-	create
-	getattr
-	setattr
-	lock
-	relabelfrom
-	relabelto
-	append
-# socket-specific
-	bind
-	connect
-	listen
-	accept
-	getopt
-	setopt
-	shutdown
-	recvfrom
-	sendto
-	recv_msg
-	send_msg
-	name_bind
-}
-
-#
-# Define a common prefix for ipc access vectors.
-#
-
-common ipc
-{
-	create
-	destroy
-	getattr
-	setattr
-	read
-	write
-	associate
-	unix_read
-	unix_write
-}
-
-#
-#  Define a common prefix for userspace database object access vectors.
-#
-
-common database
-{
-	create
-	drop
-	getattr
-	setattr
-	relabelfrom
-	relabelto
-}
-
-#
-# Define a common prefix for pointer and keyboard access vectors.
-#
-
-common x_device
-{
-	getattr
-	setattr
-	use
-	read
-	write
-	getfocus
-	setfocus
-	bell
-	force_cursor
-	freeze
-	grab
-	manage
-	list_property
-	get_property
-	set_property
-	add
-	remove
-	create
-	destroy
-}
-
-#
-# Define the access vectors.
-#
-# class class_name [ inherits common_name ] { permission_name ... }
-
-
-#
-# Define the access vector interpretation for file-related objects.
-#
-
-class filesystem
-{
-	mount
-	remount
-	unmount
-	getattr
-	relabelfrom
-	relabelto
-	transition
-	associate
-	quotamod
-	quotaget
-}
-
-class dir
-inherits file
-{
-	add_name
-	remove_name
-	reparent
-	search
-	rmdir
-	open
-	audit_access
-	execmod
-}
-
-class file
-inherits file
-{
-	execute_no_trans
-	entrypoint
-	execmod
-	open
-	audit_access
-}
-
-class lnk_file
-inherits file
-{
-	open
-	audit_access
-	execmod
-}
-
-class chr_file
-inherits file
-{
-	execute_no_trans
-	entrypoint
-	execmod
-	open
-	audit_access
-}
-
-class blk_file
-inherits file
-{
-	open
-	audit_access
-	execmod
-}
-
-class sock_file
-inherits file
-{
-	open
-	audit_access
-	execmod
-}
-
-class fifo_file
-inherits file
-{
-	open
-	audit_access
-	execmod
-}
-
-class fd
-{
-	use
-}
-
-
-#
-# Define the access vector interpretation for network-related objects.
-#
-
-class socket
-inherits socket
-
-class tcp_socket
-inherits socket
-{
-	connectto
-	newconn
-	acceptfrom
-	node_bind
-	name_connect
-}
-
-class udp_socket
-inherits socket
-{
-	node_bind
-}
-
-class rawip_socket
-inherits socket
-{
-	node_bind
-}
-
-class node
-{
-	tcp_recv
-	tcp_send
-	udp_recv
-	udp_send
-	rawip_recv
-	rawip_send
-	enforce_dest
-	dccp_recv
-	dccp_send
-	recvfrom
-	sendto
-}
-
-class netif
-{
-	tcp_recv
-	tcp_send
-	udp_recv
-	udp_send
-	rawip_recv
-	rawip_send
-	dccp_recv
-	dccp_send
-	ingress
-	egress
-}
-
-class netlink_socket
-inherits socket
-
-class packet_socket
-inherits socket
-
-class key_socket
-inherits socket
-
-class unix_stream_socket
-inherits socket
-{
-	connectto
-	newconn
-	acceptfrom
-}
-
-class unix_dgram_socket
-inherits socket
-
-#
-# Define the access vector interpretation for process-related objects
-#
-
-class process
-{
-	fork
-	transition
-	sigchld # commonly granted from child to parent
-	sigkill # cannot be caught or ignored
-	sigstop # cannot be caught or ignored
-	signull # for kill(pid, 0)
-	signal  # all other signals
-	ptrace
-	getsched
-	setsched
-	getsession
-	getpgid
-	setpgid
-	getcap
-	setcap
-	share
-	getattr
-	setexec
-	setfscreate
-	noatsecure
-	siginh
-	setrlimit
-	rlimitinh
-	dyntransition
-	setcurrent
-	execmem
-	execstack
-	execheap
-	setkeycreate
-	setsockcreate
-}
-
-
-#
-# Define the access vector interpretation for ipc-related objects
-#
-
-class ipc
-inherits ipc
-
-class sem
-inherits ipc
-
-class msgq
-inherits ipc
-{
-	enqueue
-}
-
-class msg
-{
-	send
-	receive
-}
-
-class shm
-inherits ipc
-{
-	lock
-}
-
-
-#
-# Define the access vector interpretation for the security server.
-#
-
-class security
-{
-	compute_av
-	compute_create
-	compute_member
-	check_context
-	load_policy
-	compute_relabel
-	compute_user
-	setenforce     # was avc_toggle in system class
-	setbool
-	setsecparam
-	setcheckreqprot
-	read_policy
-}
-
-
-#
-# Define the access vector interpretation for system operations.
-#
-
-class system
-{
-	ipc_info
-	syslog_read
-	syslog_mod
-	syslog_console
-	module_request
-}
-
-#
-# Define the access vector interpretation for controling capabilies
-#
-
-class capability
-{
-	# The capabilities are defined in include/linux/capability.h
-	# Capabilities >= 32 are defined in the capability2 class.
-	# Care should be taken to ensure that these are consistent with
-	# those definitions. (Order matters)
-
-	chown
-	dac_override
-	dac_read_search
-	fowner
-	fsetid
-	kill
-	setgid
-	setuid
-	setpcap
-	linux_immutable
-	net_bind_service
-	net_broadcast
-	net_admin
-	net_raw
-	ipc_lock
-	ipc_owner
-	sys_module
-	sys_rawio
-	sys_chroot
-	sys_ptrace
-	sys_pacct
-	sys_admin
-	sys_boot
-	sys_nice
-	sys_resource
-	sys_time
-	sys_tty_config
-	mknod
-	lease
-	audit_write
-	audit_control
-	setfcap
-}
-
-class capability2
-{
-	mac_override	# unused by SELinux
-	mac_admin	# unused by SELinux
-	syslog
-	wake_alarm
-	block_suspend
-}
-
-#
-# Define the access vector interpretation for controlling
-# changes to passwd information.
-#
-class passwd
-{
-	passwd	# change another user passwd
-	chfn	# change another user finger info
-	chsh	# change another user shell
-	rootok  # pam_rootok check (skip auth)
-	crontab # crontab on another user
-}
-
-#
-# SE-X Windows stuff
-#
-class x_drawable
-{
-	create
-	destroy
-	read
-	write
-	blend
-	getattr
-	setattr
-	list_child
-	add_child
-	remove_child
-	list_property
-	get_property
-	set_property
-	manage
-	override
-	show
-	hide
-	send
-	receive
-}
-
-class x_screen
-{
-	getattr
-	setattr
-	hide_cursor
-	show_cursor
-	saver_getattr
-	saver_setattr
-	saver_hide
-	saver_show
-}
-
-class x_gc
-{
-	create
-	destroy
-	getattr
-	setattr
-	use
-}
-
-class x_font
-{
-	create
-	destroy
-	getattr
-	add_glyph
-	remove_glyph
-	use
-}
-
-class x_colormap
-{
-	create
-	destroy
-	read
-	write
-	getattr
-	add_color
-	remove_color
-	install
-	uninstall
-	use
-}
-
-class x_property
-{
-	create
-	destroy
-	read
-	write
-	append
-	getattr
-	setattr
-}
-
-class x_selection
-{
-	read
-	write
-	getattr
-	setattr
-}
-
-class x_cursor
-{
-	create
-	destroy
-	read
-	write
-	getattr
-	setattr
-	use
-}
-
-class x_client
-{
-	destroy
-	getattr
-	setattr
-	manage
-}
-
-class x_device
-inherits x_device
-
-class x_server
-{
-	getattr
-	setattr
-	record
-	debug
-	grab
-	manage
-}
-
-class x_extension
-{
-	query
-	use
-}
-
-class x_resource
-{
-	read
-	write
-}
-
-class x_event
-{
-	send
-	receive
-}
-
-class x_synthetic_event
-{
-	send
-	receive
-}
-
-#
-# Extended Netlink classes
-#
-class netlink_route_socket
-inherits socket
-{
-	nlmsg_read
-	nlmsg_write
-}
-
-class netlink_firewall_socket
-inherits socket
-{
-	nlmsg_read
-	nlmsg_write
-}
-
-class netlink_tcpdiag_socket
-inherits socket
-{
-	nlmsg_read
-	nlmsg_write
-}
-
-class netlink_nflog_socket
-inherits socket
-
-class netlink_xfrm_socket
-inherits socket
-{
-	nlmsg_read
-	nlmsg_write
-}
-
-class netlink_selinux_socket
-inherits socket
-
-class netlink_audit_socket
-inherits socket
-{
-	nlmsg_read
-	nlmsg_write
-	nlmsg_relay
-	nlmsg_readpriv
-	nlmsg_tty_audit
-}
-
-class netlink_ip6fw_socket
-inherits socket
-{
-	nlmsg_read
-	nlmsg_write
-}
-
-class netlink_dnrt_socket
-inherits socket
-
-# Define the access vector interpretation for controlling
-# access and communication through the D-BUS messaging
-# system.
-#
-class dbus
-{
-	acquire_svc
-	send_msg
-}
-
-# Define the access vector interpretation for controlling
-# access through the name service cache daemon (nscd).
-#
-class nscd
-{
-	getpwd
-	getgrp
-	gethost
-	getstat
-	admin
-	shmempwd
-	shmemgrp
-	shmemhost
-	getserv
-	shmemserv
-}
-
-# Define the access vector interpretation for controlling
-# access to IPSec network data by association
-#
-class association
-{
-	sendto
-	recvfrom
-	setcontext
-	polmatch
-}
-
-# Updated Netlink class for KOBJECT_UEVENT family.
-class netlink_kobject_uevent_socket
-inherits socket
-
-class appletalk_socket
-inherits socket
-
-class packet
-{
-	send
-	recv
-	relabelto
-	flow_in		# deprecated
-	flow_out	# deprecated
-	forward_in
-	forward_out
-}
-
-class key
-{
-	view
-	read
-	write
-	search
-	link
-	setattr
-	create
-}
-
-class context
-{
-	translate
-	contains
-}
-
-class dccp_socket
-inherits socket
-{
-	node_bind
-	name_connect
-}
-
-class memprotect
-{
-	mmap_zero
-}
-
-class db_database
-inherits database
-{
-	access
-	install_module
-	load_module
-	get_param	# deprecated
-	set_param	# deprecated
-}
-
-class db_table
-inherits database
-{
-	use		# deprecated
-	select
-	update
-	insert
-	delete
-	lock
-}
-
-class db_procedure
-inherits database
-{
-	execute
-	entrypoint
-	install
-}
-
-class db_column
-inherits database
-{
-	use		# deprecated
-	select
-	update
-	insert
-}
-
-class db_tuple
-{
-	relabelfrom
-	relabelto
-	use		# deprecated
-	select
-	update
-	insert
-	delete
-}
-
-class db_blob
-inherits database
-{
-	read
-	write
-	import
-	export
-}
-
-# network peer labels
-class peer
-{
-	recv
-}
-
-class x_application_data
-{
-	paste
-	paste_after_confirm
-	copy
-}
-
-class kernel_service
-{
-	use_as_override
-	create_files_as
-}
-
-class tun_socket
-inherits socket
-
-class x_pointer
-inherits x_device
-
-class x_keyboard
-inherits x_device
-
-class db_schema
-inherits database
-{
-	search
-	add_name
-	remove_name
-}
-
-class db_view
-inherits database
-{
-	expand
-}
-
-class db_sequence
-inherits database
-{
-	get_value
-	next_value
-	set_value
-}
-
-class db_language
-inherits database
-{
-	implement
-	execute
-}
-
-class binder
-{
-	impersonate
-	call
-	set_context_mgr
-	transfer
-}
-
-class zygote
-{
-	specifyids
-	specifyrlimits
-	specifycapabilities
-	specifyinvokewith
-	specifyseinfo
-}
-
-class property_service
-{
-	set
-}
-#line 1 "external/sepolicy/global_macros"
-#####################################
-# Common groupings of object classes.
-#
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#####################################
-# Common groupings of permissions.
-#
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#####################################
-# Common socket permission sets.
-
-
-#line 1 "external/sepolicy/mls_macros"
-########################################
-#
-# gen_cats(N)
-#
-# declares categores c0 to c(N-1)
-#
-#line 10
-
-
-
-
-########################################
-#
-# gen_sens(N)
-#
-# declares sensitivites s0 to s(N-1) with dominance
-# in increasing numeric order with s0 lowest, s(N-1) highest
-#
-#line 24
-
-
-
-
-#line 34
-
-
-########################################
-#
-# gen_levels(N,M)
-#
-# levels from s0 to (N-1) with categories c0 to (M-1)
-#
-#line 45
-
-
-
-
-########################################
-#
-# Basic level names for system low and high
-#
-
-
-#line 1 "external/sepolicy/mls"
-#########################################
-# MLS declarations
-#
-
-# Generate the desired number of sensitivities and categories.
-
-#line 6
-# Each sensitivity has a name and zero or more aliases.
-#line 6
-sensitivity s0;
-#line 6
-
-#line 6
-
-#line 6
-# Define the ordering of the sensitivity levels (least to greatest)
-#line 6
-dominance { s0  }
-#line 6
-
-category c0;
-#line 7
-category c1;
-#line 7
-category c2;
-#line 7
-category c3;
-#line 7
-category c4;
-#line 7
-category c5;
-#line 7
-category c6;
-#line 7
-category c7;
-#line 7
-category c8;
-#line 7
-category c9;
-#line 7
-category c10;
-#line 7
-category c11;
-#line 7
-category c12;
-#line 7
-category c13;
-#line 7
-category c14;
-#line 7
-category c15;
-#line 7
-category c16;
-#line 7
-category c17;
-#line 7
-category c18;
-#line 7
-category c19;
-#line 7
-category c20;
-#line 7
-category c21;
-#line 7
-category c22;
-#line 7
-category c23;
-#line 7
-category c24;
-#line 7
-category c25;
-#line 7
-category c26;
-#line 7
-category c27;
-#line 7
-category c28;
-#line 7
-category c29;
-#line 7
-category c30;
-#line 7
-category c31;
-#line 7
-category c32;
-#line 7
-category c33;
-#line 7
-category c34;
-#line 7
-category c35;
-#line 7
-category c36;
-#line 7
-category c37;
-#line 7
-category c38;
-#line 7
-category c39;
-#line 7
-category c40;
-#line 7
-category c41;
-#line 7
-category c42;
-#line 7
-category c43;
-#line 7
-category c44;
-#line 7
-category c45;
-#line 7
-category c46;
-#line 7
-category c47;
-#line 7
-category c48;
-#line 7
-category c49;
-#line 7
-category c50;
-#line 7
-category c51;
-#line 7
-category c52;
-#line 7
-category c53;
-#line 7
-category c54;
-#line 7
-category c55;
-#line 7
-category c56;
-#line 7
-category c57;
-#line 7
-category c58;
-#line 7
-category c59;
-#line 7
-category c60;
-#line 7
-category c61;
-#line 7
-category c62;
-#line 7
-category c63;
-#line 7
-category c64;
-#line 7
-category c65;
-#line 7
-category c66;
-#line 7
-category c67;
-#line 7
-category c68;
-#line 7
-category c69;
-#line 7
-category c70;
-#line 7
-category c71;
-#line 7
-category c72;
-#line 7
-category c73;
-#line 7
-category c74;
-#line 7
-category c75;
-#line 7
-category c76;
-#line 7
-category c77;
-#line 7
-category c78;
-#line 7
-category c79;
-#line 7
-category c80;
-#line 7
-category c81;
-#line 7
-category c82;
-#line 7
-category c83;
-#line 7
-category c84;
-#line 7
-category c85;
-#line 7
-category c86;
-#line 7
-category c87;
-#line 7
-category c88;
-#line 7
-category c89;
-#line 7
-category c90;
-#line 7
-category c91;
-#line 7
-category c92;
-#line 7
-category c93;
-#line 7
-category c94;
-#line 7
-category c95;
-#line 7
-category c96;
-#line 7
-category c97;
-#line 7
-category c98;
-#line 7
-category c99;
-#line 7
-category c100;
-#line 7
-category c101;
-#line 7
-category c102;
-#line 7
-category c103;
-#line 7
-category c104;
-#line 7
-category c105;
-#line 7
-category c106;
-#line 7
-category c107;
-#line 7
-category c108;
-#line 7
-category c109;
-#line 7
-category c110;
-#line 7
-category c111;
-#line 7
-category c112;
-#line 7
-category c113;
-#line 7
-category c114;
-#line 7
-category c115;
-#line 7
-category c116;
-#line 7
-category c117;
-#line 7
-category c118;
-#line 7
-category c119;
-#line 7
-category c120;
-#line 7
-category c121;
-#line 7
-category c122;
-#line 7
-category c123;
-#line 7
-category c124;
-#line 7
-category c125;
-#line 7
-category c126;
-#line 7
-category c127;
-#line 7
-category c128;
-#line 7
-category c129;
-#line 7
-category c130;
-#line 7
-category c131;
-#line 7
-category c132;
-#line 7
-category c133;
-#line 7
-category c134;
-#line 7
-category c135;
-#line 7
-category c136;
-#line 7
-category c137;
-#line 7
-category c138;
-#line 7
-category c139;
-#line 7
-category c140;
-#line 7
-category c141;
-#line 7
-category c142;
-#line 7
-category c143;
-#line 7
-category c144;
-#line 7
-category c145;
-#line 7
-category c146;
-#line 7
-category c147;
-#line 7
-category c148;
-#line 7
-category c149;
-#line 7
-category c150;
-#line 7
-category c151;
-#line 7
-category c152;
-#line 7
-category c153;
-#line 7
-category c154;
-#line 7
-category c155;
-#line 7
-category c156;
-#line 7
-category c157;
-#line 7
-category c158;
-#line 7
-category c159;
-#line 7
-category c160;
-#line 7
-category c161;
-#line 7
-category c162;
-#line 7
-category c163;
-#line 7
-category c164;
-#line 7
-category c165;
-#line 7
-category c166;
-#line 7
-category c167;
-#line 7
-category c168;
-#line 7
-category c169;
-#line 7
-category c170;
-#line 7
-category c171;
-#line 7
-category c172;
-#line 7
-category c173;
-#line 7
-category c174;
-#line 7
-category c175;
-#line 7
-category c176;
-#line 7
-category c177;
-#line 7
-category c178;
-#line 7
-category c179;
-#line 7
-category c180;
-#line 7
-category c181;
-#line 7
-category c182;
-#line 7
-category c183;
-#line 7
-category c184;
-#line 7
-category c185;
-#line 7
-category c186;
-#line 7
-category c187;
-#line 7
-category c188;
-#line 7
-category c189;
-#line 7
-category c190;
-#line 7
-category c191;
-#line 7
-category c192;
-#line 7
-category c193;
-#line 7
-category c194;
-#line 7
-category c195;
-#line 7
-category c196;
-#line 7
-category c197;
-#line 7
-category c198;
-#line 7
-category c199;
-#line 7
-category c200;
-#line 7
-category c201;
-#line 7
-category c202;
-#line 7
-category c203;
-#line 7
-category c204;
-#line 7
-category c205;
-#line 7
-category c206;
-#line 7
-category c207;
-#line 7
-category c208;
-#line 7
-category c209;
-#line 7
-category c210;
-#line 7
-category c211;
-#line 7
-category c212;
-#line 7
-category c213;
-#line 7
-category c214;
-#line 7
-category c215;
-#line 7
-category c216;
-#line 7
-category c217;
-#line 7
-category c218;
-#line 7
-category c219;
-#line 7
-category c220;
-#line 7
-category c221;
-#line 7
-category c222;
-#line 7
-category c223;
-#line 7
-category c224;
-#line 7
-category c225;
-#line 7
-category c226;
-#line 7
-category c227;
-#line 7
-category c228;
-#line 7
-category c229;
-#line 7
-category c230;
-#line 7
-category c231;
-#line 7
-category c232;
-#line 7
-category c233;
-#line 7
-category c234;
-#line 7
-category c235;
-#line 7
-category c236;
-#line 7
-category c237;
-#line 7
-category c238;
-#line 7
-category c239;
-#line 7
-category c240;
-#line 7
-category c241;
-#line 7
-category c242;
-#line 7
-category c243;
-#line 7
-category c244;
-#line 7
-category c245;
-#line 7
-category c246;
-#line 7
-category c247;
-#line 7
-category c248;
-#line 7
-category c249;
-#line 7
-category c250;
-#line 7
-category c251;
-#line 7
-category c252;
-#line 7
-category c253;
-#line 7
-category c254;
-#line 7
-category c255;
-#line 7
-category c256;
-#line 7
-category c257;
-#line 7
-category c258;
-#line 7
-category c259;
-#line 7
-category c260;
-#line 7
-category c261;
-#line 7
-category c262;
-#line 7
-category c263;
-#line 7
-category c264;
-#line 7
-category c265;
-#line 7
-category c266;
-#line 7
-category c267;
-#line 7
-category c268;
-#line 7
-category c269;
-#line 7
-category c270;
-#line 7
-category c271;
-#line 7
-category c272;
-#line 7
-category c273;
-#line 7
-category c274;
-#line 7
-category c275;
-#line 7
-category c276;
-#line 7
-category c277;
-#line 7
-category c278;
-#line 7
-category c279;
-#line 7
-category c280;
-#line 7
-category c281;
-#line 7
-category c282;
-#line 7
-category c283;
-#line 7
-category c284;
-#line 7
-category c285;
-#line 7
-category c286;
-#line 7
-category c287;
-#line 7
-category c288;
-#line 7
-category c289;
-#line 7
-category c290;
-#line 7
-category c291;
-#line 7
-category c292;
-#line 7
-category c293;
-#line 7
-category c294;
-#line 7
-category c295;
-#line 7
-category c296;
-#line 7
-category c297;
-#line 7
-category c298;
-#line 7
-category c299;
-#line 7
-category c300;
-#line 7
-category c301;
-#line 7
-category c302;
-#line 7
-category c303;
-#line 7
-category c304;
-#line 7
-category c305;
-#line 7
-category c306;
-#line 7
-category c307;
-#line 7
-category c308;
-#line 7
-category c309;
-#line 7
-category c310;
-#line 7
-category c311;
-#line 7
-category c312;
-#line 7
-category c313;
-#line 7
-category c314;
-#line 7
-category c315;
-#line 7
-category c316;
-#line 7
-category c317;
-#line 7
-category c318;
-#line 7
-category c319;
-#line 7
-category c320;
-#line 7
-category c321;
-#line 7
-category c322;
-#line 7
-category c323;
-#line 7
-category c324;
-#line 7
-category c325;
-#line 7
-category c326;
-#line 7
-category c327;
-#line 7
-category c328;
-#line 7
-category c329;
-#line 7
-category c330;
-#line 7
-category c331;
-#line 7
-category c332;
-#line 7
-category c333;
-#line 7
-category c334;
-#line 7
-category c335;
-#line 7
-category c336;
-#line 7
-category c337;
-#line 7
-category c338;
-#line 7
-category c339;
-#line 7
-category c340;
-#line 7
-category c341;
-#line 7
-category c342;
-#line 7
-category c343;
-#line 7
-category c344;
-#line 7
-category c345;
-#line 7
-category c346;
-#line 7
-category c347;
-#line 7
-category c348;
-#line 7
-category c349;
-#line 7
-category c350;
-#line 7
-category c351;
-#line 7
-category c352;
-#line 7
-category c353;
-#line 7
-category c354;
-#line 7
-category c355;
-#line 7
-category c356;
-#line 7
-category c357;
-#line 7
-category c358;
-#line 7
-category c359;
-#line 7
-category c360;
-#line 7
-category c361;
-#line 7
-category c362;
-#line 7
-category c363;
-#line 7
-category c364;
-#line 7
-category c365;
-#line 7
-category c366;
-#line 7
-category c367;
-#line 7
-category c368;
-#line 7
-category c369;
-#line 7
-category c370;
-#line 7
-category c371;
-#line 7
-category c372;
-#line 7
-category c373;
-#line 7
-category c374;
-#line 7
-category c375;
-#line 7
-category c376;
-#line 7
-category c377;
-#line 7
-category c378;
-#line 7
-category c379;
-#line 7
-category c380;
-#line 7
-category c381;
-#line 7
-category c382;
-#line 7
-category c383;
-#line 7
-category c384;
-#line 7
-category c385;
-#line 7
-category c386;
-#line 7
-category c387;
-#line 7
-category c388;
-#line 7
-category c389;
-#line 7
-category c390;
-#line 7
-category c391;
-#line 7
-category c392;
-#line 7
-category c393;
-#line 7
-category c394;
-#line 7
-category c395;
-#line 7
-category c396;
-#line 7
-category c397;
-#line 7
-category c398;
-#line 7
-category c399;
-#line 7
-category c400;
-#line 7
-category c401;
-#line 7
-category c402;
-#line 7
-category c403;
-#line 7
-category c404;
-#line 7
-category c405;
-#line 7
-category c406;
-#line 7
-category c407;
-#line 7
-category c408;
-#line 7
-category c409;
-#line 7
-category c410;
-#line 7
-category c411;
-#line 7
-category c412;
-#line 7
-category c413;
-#line 7
-category c414;
-#line 7
-category c415;
-#line 7
-category c416;
-#line 7
-category c417;
-#line 7
-category c418;
-#line 7
-category c419;
-#line 7
-category c420;
-#line 7
-category c421;
-#line 7
-category c422;
-#line 7
-category c423;
-#line 7
-category c424;
-#line 7
-category c425;
-#line 7
-category c426;
-#line 7
-category c427;
-#line 7
-category c428;
-#line 7
-category c429;
-#line 7
-category c430;
-#line 7
-category c431;
-#line 7
-category c432;
-#line 7
-category c433;
-#line 7
-category c434;
-#line 7
-category c435;
-#line 7
-category c436;
-#line 7
-category c437;
-#line 7
-category c438;
-#line 7
-category c439;
-#line 7
-category c440;
-#line 7
-category c441;
-#line 7
-category c442;
-#line 7
-category c443;
-#line 7
-category c444;
-#line 7
-category c445;
-#line 7
-category c446;
-#line 7
-category c447;
-#line 7
-category c448;
-#line 7
-category c449;
-#line 7
-category c450;
-#line 7
-category c451;
-#line 7
-category c452;
-#line 7
-category c453;
-#line 7
-category c454;
-#line 7
-category c455;
-#line 7
-category c456;
-#line 7
-category c457;
-#line 7
-category c458;
-#line 7
-category c459;
-#line 7
-category c460;
-#line 7
-category c461;
-#line 7
-category c462;
-#line 7
-category c463;
-#line 7
-category c464;
-#line 7
-category c465;
-#line 7
-category c466;
-#line 7
-category c467;
-#line 7
-category c468;
-#line 7
-category c469;
-#line 7
-category c470;
-#line 7
-category c471;
-#line 7
-category c472;
-#line 7
-category c473;
-#line 7
-category c474;
-#line 7
-category c475;
-#line 7
-category c476;
-#line 7
-category c477;
-#line 7
-category c478;
-#line 7
-category c479;
-#line 7
-category c480;
-#line 7
-category c481;
-#line 7
-category c482;
-#line 7
-category c483;
-#line 7
-category c484;
-#line 7
-category c485;
-#line 7
-category c486;
-#line 7
-category c487;
-#line 7
-category c488;
-#line 7
-category c489;
-#line 7
-category c490;
-#line 7
-category c491;
-#line 7
-category c492;
-#line 7
-category c493;
-#line 7
-category c494;
-#line 7
-category c495;
-#line 7
-category c496;
-#line 7
-category c497;
-#line 7
-category c498;
-#line 7
-category c499;
-#line 7
-category c500;
-#line 7
-category c501;
-#line 7
-category c502;
-#line 7
-category c503;
-#line 7
-category c504;
-#line 7
-category c505;
-#line 7
-category c506;
-#line 7
-category c507;
-#line 7
-category c508;
-#line 7
-category c509;
-#line 7
-category c510;
-#line 7
-category c511;
-#line 7
-category c512;
-#line 7
-category c513;
-#line 7
-category c514;
-#line 7
-category c515;
-#line 7
-category c516;
-#line 7
-category c517;
-#line 7
-category c518;
-#line 7
-category c519;
-#line 7
-category c520;
-#line 7
-category c521;
-#line 7
-category c522;
-#line 7
-category c523;
-#line 7
-category c524;
-#line 7
-category c525;
-#line 7
-category c526;
-#line 7
-category c527;
-#line 7
-category c528;
-#line 7
-category c529;
-#line 7
-category c530;
-#line 7
-category c531;
-#line 7
-category c532;
-#line 7
-category c533;
-#line 7
-category c534;
-#line 7
-category c535;
-#line 7
-category c536;
-#line 7
-category c537;
-#line 7
-category c538;
-#line 7
-category c539;
-#line 7
-category c540;
-#line 7
-category c541;
-#line 7
-category c542;
-#line 7
-category c543;
-#line 7
-category c544;
-#line 7
-category c545;
-#line 7
-category c546;
-#line 7
-category c547;
-#line 7
-category c548;
-#line 7
-category c549;
-#line 7
-category c550;
-#line 7
-category c551;
-#line 7
-category c552;
-#line 7
-category c553;
-#line 7
-category c554;
-#line 7
-category c555;
-#line 7
-category c556;
-#line 7
-category c557;
-#line 7
-category c558;
-#line 7
-category c559;
-#line 7
-category c560;
-#line 7
-category c561;
-#line 7
-category c562;
-#line 7
-category c563;
-#line 7
-category c564;
-#line 7
-category c565;
-#line 7
-category c566;
-#line 7
-category c567;
-#line 7
-category c568;
-#line 7
-category c569;
-#line 7
-category c570;
-#line 7
-category c571;
-#line 7
-category c572;
-#line 7
-category c573;
-#line 7
-category c574;
-#line 7
-category c575;
-#line 7
-category c576;
-#line 7
-category c577;
-#line 7
-category c578;
-#line 7
-category c579;
-#line 7
-category c580;
-#line 7
-category c581;
-#line 7
-category c582;
-#line 7
-category c583;
-#line 7
-category c584;
-#line 7
-category c585;
-#line 7
-category c586;
-#line 7
-category c587;
-#line 7
-category c588;
-#line 7
-category c589;
-#line 7
-category c590;
-#line 7
-category c591;
-#line 7
-category c592;
-#line 7
-category c593;
-#line 7
-category c594;
-#line 7
-category c595;
-#line 7
-category c596;
-#line 7
-category c597;
-#line 7
-category c598;
-#line 7
-category c599;
-#line 7
-category c600;
-#line 7
-category c601;
-#line 7
-category c602;
-#line 7
-category c603;
-#line 7
-category c604;
-#line 7
-category c605;
-#line 7
-category c606;
-#line 7
-category c607;
-#line 7
-category c608;
-#line 7
-category c609;
-#line 7
-category c610;
-#line 7
-category c611;
-#line 7
-category c612;
-#line 7
-category c613;
-#line 7
-category c614;
-#line 7
-category c615;
-#line 7
-category c616;
-#line 7
-category c617;
-#line 7
-category c618;
-#line 7
-category c619;
-#line 7
-category c620;
-#line 7
-category c621;
-#line 7
-category c622;
-#line 7
-category c623;
-#line 7
-category c624;
-#line 7
-category c625;
-#line 7
-category c626;
-#line 7
-category c627;
-#line 7
-category c628;
-#line 7
-category c629;
-#line 7
-category c630;
-#line 7
-category c631;
-#line 7
-category c632;
-#line 7
-category c633;
-#line 7
-category c634;
-#line 7
-category c635;
-#line 7
-category c636;
-#line 7
-category c637;
-#line 7
-category c638;
-#line 7
-category c639;
-#line 7
-category c640;
-#line 7
-category c641;
-#line 7
-category c642;
-#line 7
-category c643;
-#line 7
-category c644;
-#line 7
-category c645;
-#line 7
-category c646;
-#line 7
-category c647;
-#line 7
-category c648;
-#line 7
-category c649;
-#line 7
-category c650;
-#line 7
-category c651;
-#line 7
-category c652;
-#line 7
-category c653;
-#line 7
-category c654;
-#line 7
-category c655;
-#line 7
-category c656;
-#line 7
-category c657;
-#line 7
-category c658;
-#line 7
-category c659;
-#line 7
-category c660;
-#line 7
-category c661;
-#line 7
-category c662;
-#line 7
-category c663;
-#line 7
-category c664;
-#line 7
-category c665;
-#line 7
-category c666;
-#line 7
-category c667;
-#line 7
-category c668;
-#line 7
-category c669;
-#line 7
-category c670;
-#line 7
-category c671;
-#line 7
-category c672;
-#line 7
-category c673;
-#line 7
-category c674;
-#line 7
-category c675;
-#line 7
-category c676;
-#line 7
-category c677;
-#line 7
-category c678;
-#line 7
-category c679;
-#line 7
-category c680;
-#line 7
-category c681;
-#line 7
-category c682;
-#line 7
-category c683;
-#line 7
-category c684;
-#line 7
-category c685;
-#line 7
-category c686;
-#line 7
-category c687;
-#line 7
-category c688;
-#line 7
-category c689;
-#line 7
-category c690;
-#line 7
-category c691;
-#line 7
-category c692;
-#line 7
-category c693;
-#line 7
-category c694;
-#line 7
-category c695;
-#line 7
-category c696;
-#line 7
-category c697;
-#line 7
-category c698;
-#line 7
-category c699;
-#line 7
-category c700;
-#line 7
-category c701;
-#line 7
-category c702;
-#line 7
-category c703;
-#line 7
-category c704;
-#line 7
-category c705;
-#line 7
-category c706;
-#line 7
-category c707;
-#line 7
-category c708;
-#line 7
-category c709;
-#line 7
-category c710;
-#line 7
-category c711;
-#line 7
-category c712;
-#line 7
-category c713;
-#line 7
-category c714;
-#line 7
-category c715;
-#line 7
-category c716;
-#line 7
-category c717;
-#line 7
-category c718;
-#line 7
-category c719;
-#line 7
-category c720;
-#line 7
-category c721;
-#line 7
-category c722;
-#line 7
-category c723;
-#line 7
-category c724;
-#line 7
-category c725;
-#line 7
-category c726;
-#line 7
-category c727;
-#line 7
-category c728;
-#line 7
-category c729;
-#line 7
-category c730;
-#line 7
-category c731;
-#line 7
-category c732;
-#line 7
-category c733;
-#line 7
-category c734;
-#line 7
-category c735;
-#line 7
-category c736;
-#line 7
-category c737;
-#line 7
-category c738;
-#line 7
-category c739;
-#line 7
-category c740;
-#line 7
-category c741;
-#line 7
-category c742;
-#line 7
-category c743;
-#line 7
-category c744;
-#line 7
-category c745;
-#line 7
-category c746;
-#line 7
-category c747;
-#line 7
-category c748;
-#line 7
-category c749;
-#line 7
-category c750;
-#line 7
-category c751;
-#line 7
-category c752;
-#line 7
-category c753;
-#line 7
-category c754;
-#line 7
-category c755;
-#line 7
-category c756;
-#line 7
-category c757;
-#line 7
-category c758;
-#line 7
-category c759;
-#line 7
-category c760;
-#line 7
-category c761;
-#line 7
-category c762;
-#line 7
-category c763;
-#line 7
-category c764;
-#line 7
-category c765;
-#line 7
-category c766;
-#line 7
-category c767;
-#line 7
-category c768;
-#line 7
-category c769;
-#line 7
-category c770;
-#line 7
-category c771;
-#line 7
-category c772;
-#line 7
-category c773;
-#line 7
-category c774;
-#line 7
-category c775;
-#line 7
-category c776;
-#line 7
-category c777;
-#line 7
-category c778;
-#line 7
-category c779;
-#line 7
-category c780;
-#line 7
-category c781;
-#line 7
-category c782;
-#line 7
-category c783;
-#line 7
-category c784;
-#line 7
-category c785;
-#line 7
-category c786;
-#line 7
-category c787;
-#line 7
-category c788;
-#line 7
-category c789;
-#line 7
-category c790;
-#line 7
-category c791;
-#line 7
-category c792;
-#line 7
-category c793;
-#line 7
-category c794;
-#line 7
-category c795;
-#line 7
-category c796;
-#line 7
-category c797;
-#line 7
-category c798;
-#line 7
-category c799;
-#line 7
-category c800;
-#line 7
-category c801;
-#line 7
-category c802;
-#line 7
-category c803;
-#line 7
-category c804;
-#line 7
-category c805;
-#line 7
-category c806;
-#line 7
-category c807;
-#line 7
-category c808;
-#line 7
-category c809;
-#line 7
-category c810;
-#line 7
-category c811;
-#line 7
-category c812;
-#line 7
-category c813;
-#line 7
-category c814;
-#line 7
-category c815;
-#line 7
-category c816;
-#line 7
-category c817;
-#line 7
-category c818;
-#line 7
-category c819;
-#line 7
-category c820;
-#line 7
-category c821;
-#line 7
-category c822;
-#line 7
-category c823;
-#line 7
-category c824;
-#line 7
-category c825;
-#line 7
-category c826;
-#line 7
-category c827;
-#line 7
-category c828;
-#line 7
-category c829;
-#line 7
-category c830;
-#line 7
-category c831;
-#line 7
-category c832;
-#line 7
-category c833;
-#line 7
-category c834;
-#line 7
-category c835;
-#line 7
-category c836;
-#line 7
-category c837;
-#line 7
-category c838;
-#line 7
-category c839;
-#line 7
-category c840;
-#line 7
-category c841;
-#line 7
-category c842;
-#line 7
-category c843;
-#line 7
-category c844;
-#line 7
-category c845;
-#line 7
-category c846;
-#line 7
-category c847;
-#line 7
-category c848;
-#line 7
-category c849;
-#line 7
-category c850;
-#line 7
-category c851;
-#line 7
-category c852;
-#line 7
-category c853;
-#line 7
-category c854;
-#line 7
-category c855;
-#line 7
-category c856;
-#line 7
-category c857;
-#line 7
-category c858;
-#line 7
-category c859;
-#line 7
-category c860;
-#line 7
-category c861;
-#line 7
-category c862;
-#line 7
-category c863;
-#line 7
-category c864;
-#line 7
-category c865;
-#line 7
-category c866;
-#line 7
-category c867;
-#line 7
-category c868;
-#line 7
-category c869;
-#line 7
-category c870;
-#line 7
-category c871;
-#line 7
-category c872;
-#line 7
-category c873;
-#line 7
-category c874;
-#line 7
-category c875;
-#line 7
-category c876;
-#line 7
-category c877;
-#line 7
-category c878;
-#line 7
-category c879;
-#line 7
-category c880;
-#line 7
-category c881;
-#line 7
-category c882;
-#line 7
-category c883;
-#line 7
-category c884;
-#line 7
-category c885;
-#line 7
-category c886;
-#line 7
-category c887;
-#line 7
-category c888;
-#line 7
-category c889;
-#line 7
-category c890;
-#line 7
-category c891;
-#line 7
-category c892;
-#line 7
-category c893;
-#line 7
-category c894;
-#line 7
-category c895;
-#line 7
-category c896;
-#line 7
-category c897;
-#line 7
-category c898;
-#line 7
-category c899;
-#line 7
-category c900;
-#line 7
-category c901;
-#line 7
-category c902;
-#line 7
-category c903;
-#line 7
-category c904;
-#line 7
-category c905;
-#line 7
-category c906;
-#line 7
-category c907;
-#line 7
-category c908;
-#line 7
-category c909;
-#line 7
-category c910;
-#line 7
-category c911;
-#line 7
-category c912;
-#line 7
-category c913;
-#line 7
-category c914;
-#line 7
-category c915;
-#line 7
-category c916;
-#line 7
-category c917;
-#line 7
-category c918;
-#line 7
-category c919;
-#line 7
-category c920;
-#line 7
-category c921;
-#line 7
-category c922;
-#line 7
-category c923;
-#line 7
-category c924;
-#line 7
-category c925;
-#line 7
-category c926;
-#line 7
-category c927;
-#line 7
-category c928;
-#line 7
-category c929;
-#line 7
-category c930;
-#line 7
-category c931;
-#line 7
-category c932;
-#line 7
-category c933;
-#line 7
-category c934;
-#line 7
-category c935;
-#line 7
-category c936;
-#line 7
-category c937;
-#line 7
-category c938;
-#line 7
-category c939;
-#line 7
-category c940;
-#line 7
-category c941;
-#line 7
-category c942;
-#line 7
-category c943;
-#line 7
-category c944;
-#line 7
-category c945;
-#line 7
-category c946;
-#line 7
-category c947;
-#line 7
-category c948;
-#line 7
-category c949;
-#line 7
-category c950;
-#line 7
-category c951;
-#line 7
-category c952;
-#line 7
-category c953;
-#line 7
-category c954;
-#line 7
-category c955;
-#line 7
-category c956;
-#line 7
-category c957;
-#line 7
-category c958;
-#line 7
-category c959;
-#line 7
-category c960;
-#line 7
-category c961;
-#line 7
-category c962;
-#line 7
-category c963;
-#line 7
-category c964;
-#line 7
-category c965;
-#line 7
-category c966;
-#line 7
-category c967;
-#line 7
-category c968;
-#line 7
-category c969;
-#line 7
-category c970;
-#line 7
-category c971;
-#line 7
-category c972;
-#line 7
-category c973;
-#line 7
-category c974;
-#line 7
-category c975;
-#line 7
-category c976;
-#line 7
-category c977;
-#line 7
-category c978;
-#line 7
-category c979;
-#line 7
-category c980;
-#line 7
-category c981;
-#line 7
-category c982;
-#line 7
-category c983;
-#line 7
-category c984;
-#line 7
-category c985;
-#line 7
-category c986;
-#line 7
-category c987;
-#line 7
-category c988;
-#line 7
-category c989;
-#line 7
-category c990;
-#line 7
-category c991;
-#line 7
-category c992;
-#line 7
-category c993;
-#line 7
-category c994;
-#line 7
-category c995;
-#line 7
-category c996;
-#line 7
-category c997;
-#line 7
-category c998;
-#line 7
-category c999;
-#line 7
-category c1000;
-#line 7
-category c1001;
-#line 7
-category c1002;
-#line 7
-category c1003;
-#line 7
-category c1004;
-#line 7
-category c1005;
-#line 7
-category c1006;
-#line 7
-category c1007;
-#line 7
-category c1008;
-#line 7
-category c1009;
-#line 7
-category c1010;
-#line 7
-category c1011;
-#line 7
-category c1012;
-#line 7
-category c1013;
-#line 7
-category c1014;
-#line 7
-category c1015;
-#line 7
-category c1016;
-#line 7
-category c1017;
-#line 7
-category c1018;
-#line 7
-category c1019;
-#line 7
-category c1020;
-#line 7
-category c1021;
-#line 7
-category c1022;
-#line 7
-category c1023;
-#line 7
-
-
-# Generate level definitions for each sensitivity and category.
-level s0:c0.c1023;
-#line 10
-
-
-
-#################################################
-# MLS policy constraints
-#
-
-#
-# Process constraints
-#
-
-# Process transition:  Require equivalence unless the subject is trusted.
-mlsconstrain process { transition dyntransition }
-	     ((h1 eq h2 and l1 eq l2) or t1 == mlstrustedsubject);
-
-# Process read operations: No read up unless trusted.
-mlsconstrain process { getsched getsession getpgid getcap getattr ptrace share }
-	     (l1 dom l2 or t1 == mlstrustedsubject);
-
-# Process write operations:  No write down unless trusted.
-mlsconstrain process { sigkill sigstop signal setsched setpgid setcap setrlimit ptrace share }
-	     (l1 domby l2 or t1 == mlstrustedsubject);
-
-#
-# Socket constraints
-#
-
-# Create/relabel operations:  Subject must be equivalent to object unless
-# the subject is trusted.  Sockets inherit the range of their creator.
-mlsconstrain { socket tcp_socket udp_socket rawip_socket netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket appletalk_socket netlink_route_socket netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket netlink_ip6fw_socket netlink_dnrt_socket netlink_kobject_uevent_socket tun_socket } { create relabelfrom relabelto }
-	     ((h1 eq h2 and l1 eq l2) or t1 == mlstrustedsubject);
-
-# Datagram send: Sender must be dominated by receiver unless one of them is
-# trusted.
-mlsconstrain unix_dgram_socket { sendto }
-	     (l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedsubject);
-
-# Stream connect:  Client must be equivalent to server unless one of them
-# is trusted.
-mlsconstrain unix_stream_socket { connectto }
-	     (l1 eq l2 or t1 == mlstrustedsubject or t2 == mlstrustedsubject);
-
-#
-# Directory/file constraints
-#
-
-# Create/relabel operations:  Subject must be equivalent to object unless
-# the subject is trusted. Also, files should always be single-level.
-# Do NOT exempt mlstrustedobject types from this constraint.
-mlsconstrain { dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } { create relabelfrom relabelto }
-	     (l2 eq h2 and (l1 eq l2 or t1 == mlstrustedsubject));
-
-#
-# Constraints for app data files only.
-#
-
-# Only constrain open, not read/write.
-# Also constrain other forms of manipulation, e.g. chmod/chown, unlink, rename, etc.
-# Subject must be equivalent to object unless the subject is trusted.
-mlsconstrain dir { open search setattr rename add_name remove_name reparent rmdir }
-	     (t2 != app_data_file or l1 eq l2 or t1 == mlstrustedsubject);
-mlsconstrain { file lnk_file sock_file } { open setattr unlink link rename }
-	     (t2 != app_data_file or l1 eq l2 or t1 == mlstrustedsubject);
-
-#
-# Constraints for file types other than app data files.
-#
-
-# Read operations: Subject must dominate object unless the subject
-# or the object is trusted.
-mlsconstrain dir { read getattr search }
-	     (t2 == app_data_file or l1 dom l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-mlsconstrain { file lnk_file sock_file chr_file blk_file } { read getattr execute }
-	     (t2 == app_data_file or l1 dom l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-# Write operations: Subject must be dominated by the object unless the
-# subject or the object is trusted.
-mlsconstrain dir { write setattr rename add_name remove_name reparent rmdir }
-	     (t2 == app_data_file or l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-mlsconstrain { file lnk_file sock_file chr_file blk_file } { write setattr append unlink link rename }
-	     (t2 == app_data_file or l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-# Special case for FIFOs.
-# These can be unnamed pipes, in which case they will be labeled with the
-# creating process' label. Thus we also have an exemption when the "object"
-# is a MLS trusted subject and can receive data at any level.
-mlsconstrain fifo_file { read getattr }
-	     (l1 dom l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject or t2 == mlstrustedsubject);
-
-mlsconstrain fifo_file { write setattr append unlink link rename }
-	     (l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject or t2 == mlstrustedsubject);
-
-#
-# IPC constraints
-#
-
-# Create/destroy: equivalence or trusted.
-mlsconstrain { sem msgq shm ipc } { create destroy }
-	     (l2 eq h2 and (l1 eq l2 or t1 == mlstrustedsubject));
-
-# Read ops: No read up unless trusted.
-mlsconstrain { sem msgq shm ipc } { getattr read associate unix_read }
-	     (l1 dom l2 or t1 == mlstrustedsubject);
-
-# Write ops: No write down unless trusted.
-mlsconstrain { sem msgq shm ipc } { write unix_write }
-	     (l1 domby l2 or t1 == mlstrustedsubject);
-
-#
-# Binder IPC constraints
-#
-# Presently commented out, as apps are expected to call one another.
-# This would only make sense if apps were assigned categories
-# based on allowable communications rather than per-app categories.
-#mlsconstrain binder call
-#	(l1 eq l2 or t1 == mlstrustedsubject or t2 == mlstrustedsubject);
-#line 1 "external/sepolicy/policy_capabilities"
-# Enable new networking controls.
-policycap network_peer_controls;
-
-# Enable open permission check.
-policycap open_perms;
-#line 1 "external/sepolicy/te_macros"
-#####################################
-# domain_trans(olddomain, type, newdomain)
-# Allow a transition from olddomain to newdomain
-# upon executing a file labeled with type.
-# This only allows the transition; it does not
-# cause it to occur automatically - use domain_auto_trans
-# if that is what you want.
-#
-#line 21
-
-
-#####################################
-# domain_auto_trans(olddomain, type, newdomain)
-# Automatically transition from olddomain to newdomain
-# upon executing a file labeled with type.
-#
-#line 33
-
-
-#####################################
-# file_type_trans(domain, dir_type, file_type)
-# Allow domain to create a file labeled file_type in a
-# directory labeled dir_type.
-# This only allows the transition; it does not
-# cause it to occur automatically - use file_type_auto_trans
-# if that is what you want.
-#
-#line 49
-
-
-#####################################
-# file_type_auto_trans(domain, dir_type, file_type)
-# Automatically label new files with file_type when
-# they are created by domain in directories labeled dir_type.
-#
-#line 62
-
-
-#####################################
-# r_dir_file(domain, type)
-# Allow the specified domain to read directories, files
-# and symbolic links of the specified type.
-#line 71
-
-
-#####################################
-# unconfined_domain(domain)
-# Allow the specified domain to perform more privileged operations
-# than would be typically allowed. Please see the comments at the
-# top of unconfined.te.
-#
-#line 82
-
-
-#####################################
-# tmpfs_domain(domain)
-# Define and allow access to a unique type for
-# this domain when creating tmpfs / shmem / ashmem files.
-#line 92
-
-
-#####################################
-# init_daemon_domain(domain)
-# Set up a transition from init to the daemon domain
-# upon executing its binary.
-#line 101
-
-
-#####################################
-# app_domain(domain)
-# Allow a base set of permissions required for all apps.
-#line 112
-
-
-#####################################
-# relabelto_domain(domain)
-# Allows this domain to use the relabelto permission
-#line 119
-
-
-#####################################
-# platform_app_domain(domain)
-# Allow permissions specific to platform apps.
-#line 127
-
-
-#####################################
-# net_domain(domain)
-# Allow a base set of permissions required for network access.
-#line 134
-
-
-#####################################
-# bluetooth_domain(domain)
-# Allow a base set of permissions required for bluetooth access.
-#line 141
-
-
-#####################################
-# unix_socket_connect(clientdomain, socket, serverdomain)
-# Allow a local socket connection from clientdomain via
-# socket to serverdomain.
-#line 150
-
-
-#####################################
-# unix_socket_send(clientdomain, socket, serverdomain)
-# Allow a local socket send from clientdomain via
-# socket to serverdomain.
-#line 159
-
-
-#####################################
-# binder_use(domain)
-# Allow domain to use Binder IPC.
-#line 169
-
-
-#####################################
-# binder_call(clientdomain, serverdomain)
-# Allow clientdomain to perform binder IPC to serverdomain.
-#line 181
-
-
-#####################################
-# binder_service(domain)
-# Mark a domain as being a Binder service domain.
-# Used to allow binder IPC to the various system services.
-#line 189
-
-
-#####################################
-# selinux_check_access(domain)
-# Allow domain to check SELinux permissions via selinuxfs.
-#line 199
-
-
-#####################################
-# selinux_check_context(domain)
-# Allow domain to check SELinux contexts via selinuxfs.
-#line 208
-
-
-#####################################
-# selinux_getenforce(domain)
-# Allow domain to check whether SELinux is enforcing.
-#line 216
-
-
-#####################################
-# selinux_setenforce(domain)
-# Allow domain to set SELinux to enforcing.
-#line 225
-
-
-#####################################
-# selinux_setbool(domain)
-# Allow domain to set SELinux booleans.
-#line 234
-
-
-#####################################
-# security_access_policy(domain)
-# Read only access to all policy files and
-# selinuxfs
-#line 248
-
-
-#####################################
-# selinux_manage_policy(domain)
-# Ability to manage policy files and
-# trigger runtime reload.
-#line 261
-
-
-#####################################
-# mmac_manage_policy(domain)
-# Ability to manage mmac policy files,
-# trigger runtime reload, change
-# mmac enforcing mode and access logcat.
-#line 274
-
-
-#####################################
-# access_kmsg(domain)
-# Ability to read from kernel logs
-# and execute the klogctl syscall
-# in a non destructive manner. See
-# man 2 klogctl
-#line 284
-
-
-#####################################
-# write_klog(domain)
-# Ability to write to kernel log via
-# klog_write()
-# See system/core/libcutil/klog.c
-#line 295
-
-
-#####################################
-# create_pty(domain)
-# Allow domain to create and use a pty, isolated from any other domain ptys.
-#line 309
-
-
-#####################################
-# Non system_app application set
-#
-
-
-#####################################
-# Userdebug or eng builds
-# SELinux rules which apply only to userdebug or eng builds
-#
-
-
-#####################################
-# permissive_or_unconfined
-# Returns "permissive $1" if FORCE_PERMISSIVE_TO_UNCONFINED is false,
-# and "unconfined($1)" otherwise.
-#
-# This is used for experimental domains, where we want to ensure
-# the domain is unconfined+enforcing once new SELinux policy development
-# has ceased.
-#
-
-
-#####################################
-# write_logd(domain)
-# Ability to write to android log
-# daemon via sockets
-#line 345
-
-
-#####################################
-# read_logd(domain)
-# Ability to read from android
-# log daemon via sockets
-#line 353
-
-
-#####################################
-# control_logd(domain)
-# Ability to control
-# android log daemon via sockets
-#line 363
-
-#line 1 "external/sepolicy/attributes"
-######################################
-# Attribute declarations
-#
-
-# All types used for devices.
-attribute dev_type;
-
-# All types used for processes.
-attribute domain;
-
-# All types used for filesystems.
-attribute fs_type;
-
-# All types used for files that can exist on a labeled fs.
-# Do not use for pseudo file types.
-attribute file_type;
-
-# All types used for domain entry points.
-attribute exec_type;
-
-# All types used for /data files.
-attribute data_file_type;
-
-# All types use for sysfs files.
-attribute sysfs_type;
-
-# Attribute used for all sdcards
-attribute sdcard_type;
-
-# All types used for nodes/hosts.
-attribute node_type;
-
-# All types used for network interfaces.
-attribute netif_type;
-
-# All types used for network ports.
-attribute port_type;
-
-# All types used for property service
-attribute property_type;
-
-# All domains that can override MLS restrictions.
-# i.e. processes that can read up and write down.
-attribute mlstrustedsubject;
-
-# All types that can override MLS restrictions.
-# i.e. files that can be read by lower and written by higher
-attribute mlstrustedobject;
-
-# Domains that are allowed all permissions ("unconfined").
-attribute unconfineddomain;
-
-# All domains used for shells.
-attribute shelldomain;
-
-# All domains used for apps.
-attribute appdomain;
-
-# All domains used for apps with network access.
-attribute netdomain;
-
-# All domains used for apps with bluetooth access.
-attribute bluetoothdomain;
-
-# All domains used for binder service domains.
-attribute binderservicedomain;
-
-# Allow domains used for platform (signed by build key) apps.
-attribute platformappdomain;
-
-# All domains which are allowed the "relabelto" permission
-attribute relabeltodomain;
-#line 1 "external/sepolicy/adbd.te"
-# adbd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type adbd, domain;
-
-#line 7
-
-
-
-#line 9
-# Allow the necessary permissions.
-#line 9
-
-#line 9
-# Old domain may exec the file and transition to the new domain.
-#line 9
-allow adbd shell_exec:file { getattr open read execute };
-#line 9
-allow adbd shell:process transition;
-#line 9
-# New domain is entered by executing the file.
-#line 9
-allow shell shell_exec:file { entrypoint read execute };
-#line 9
-# New domain can send SIGCHLD to its caller.
-#line 9
-allow shell adbd:process sigchld;
-#line 9
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 9
-dontaudit adbd shell:process noatsecure;
-#line 9
-# XXX dontaudit candidate but requires further study.
-#line 9
-allow adbd shell:process { siginh rlimitinh };
-#line 9
-
-#line 9
-# Make the transition occur by default.
-#line 9
-type_transition adbd shell_exec:process shell;
-#line 9
-
-# this is an entrypoint
-allow adbd rootfs:file entrypoint;
-
-# Do not sanitize the environment or open fds of the shell.
-allow adbd shell:process noatsecure;
-
-# Set UID and GID to shell.  Set supplementary groups.
-allow adbd self:capability { setuid setgid };
-
-# Drop capabilities from bounding set on user builds.
-allow adbd self:capability setpcap;
-
-# Create and use network sockets.
-
-#line 23
-typeattribute adbd netdomain;
-#line 23
-
-
-# Access /dev/android_adb.
-allow adbd adb_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# On emulator, access /dev/qemu*.
-allow adbd qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Use a pseudo tty.
-allow adbd devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# adb push/pull /data/local/tmp.
-allow adbd shell_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow adbd shell_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# adb push/pull sdcard.
-allow adbd sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow adbd sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Set service.adb.*, sys.powerctl properties.
-
-#line 43
-allow adbd property_socket:sock_file write;
-#line 43
-allow adbd init:unix_stream_socket connectto;
-#line 43
-
-allow adbd shell_prop:property_service set;
-allow adbd powerctl_prop:property_service set;
-
-# XXX Run /system/bin/vdc to connect to vold.  Run in a separate domain?
-# Also covers running /system/bin/bu.
-allow adbd system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-#line 50
-allow adbd vold_socket:sock_file write;
-#line 50
-allow adbd vold:unix_stream_socket connectto;
-#line 50
-
-
-# Perform binder IPC to surfaceflinger (screencap)
-# XXX Run screencap in a separate domain?
-
-#line 54
-# Call the servicemanager and transfer references to it.
-#line 54
-allow adbd servicemanager:binder { call transfer };
-#line 54
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 54
-# all domains in domain.te.
-#line 54
-
-
-#line 55
-# Call the server domain and optionally transfer references to it.
-#line 55
-allow adbd surfaceflinger:binder { call transfer };
-#line 55
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 55
-allow surfaceflinger adbd:binder transfer;
-#line 55
-# Receive and use open files from the server.
-#line 55
-allow adbd surfaceflinger:fd use;
-#line 55
-
-
-# Read /data/misc/adb/adb_keys.
-allow adbd adb_keys_file:dir search;
-allow adbd adb_keys_file:file { getattr open read ioctl lock };
-
-# Allow access in case /data/misc/adb still has the old type.
-allow adbd system_data_file:dir search;
-allow adbd system_data_file:file { getattr open read ioctl lock };
-
-# ndk-gdb invokes adb forward to forward the gdbserver socket.
-allow adbd app_data_file:dir search;
-allow adbd app_data_file:sock_file write;
-allow adbd appdomain:unix_stream_socket connectto;
-
-# ndk-gdb invokes adb pull of app_process, linker, and libc.so.
-allow adbd zygote_exec:file { getattr open read ioctl lock };
-allow adbd system_file:file { getattr open read ioctl lock };
-#line 1 "external/sepolicy/app.te"
-###
-### Domain for all zygote spawned apps
-###
-### This file is the base policy for all zygote spawned apps.
-### Other policy files, such as isolated_app.te, untrusted_app.te, etc
-### extend from this policy. Only policies which should apply to ALL
-### zygote spawned apps should be added here.
-###
-
-# Dalvik Compiler JIT Mapping.
-allow appdomain self:process execmem;
-allow appdomain ashmem_device:chr_file execute;
-
-# Allow apps to connect to the keystore
-
-#line 15
-allow appdomain keystore_socket:sock_file write;
-#line 15
-allow appdomain keystore:unix_stream_socket connectto;
-#line 15
-
-
-# Receive and use open file descriptors inherited from zygote.
-allow appdomain zygote:fd use;
-
-# gdbserver for ndk-gdb reads the zygote.
-allow appdomain zygote_exec:file { getattr open read ioctl lock };
-
-# gdbserver for ndk-gdb ptrace attaches to app process.
-allow appdomain self:process ptrace;
-
-# Read system properties managed by zygote.
-allow appdomain zygote_tmpfs:file read;
-
-# Notify zygote of death;
-allow appdomain zygote:process sigchld;
-
-# Notify shell and adbd of death when spawned via runas for ndk-gdb.
-allow appdomain shell:process sigchld;
-allow appdomain adbd:process sigchld;
-
-# child shell or gdbserver pty access for runas.
-allow appdomain devpts:chr_file { getattr read write ioctl };
-
-# Communicate with system_server.
-allow appdomain system_server:fifo_file { { getattr open read ioctl lock } { open append write } };
-allow appdomain system_server:unix_stream_socket { read write setopt };
-
-#line 42
-# Call the server domain and optionally transfer references to it.
-#line 42
-allow appdomain system_server:binder { call transfer };
-#line 42
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 42
-allow system_server appdomain:binder transfer;
-#line 42
-# Receive and use open files from the server.
-#line 42
-allow appdomain system_server:fd use;
-#line 42
-
-
-# Communication with other apps via fifos
-allow appdomain appdomain:fifo_file { { getattr open read ioctl lock } { open append write } };
-
-# Communicate with surfaceflinger.
-allow appdomain surfaceflinger:unix_stream_socket { read write setopt };
-
-#line 49
-# Call the server domain and optionally transfer references to it.
-#line 49
-allow appdomain surfaceflinger:binder { call transfer };
-#line 49
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 49
-allow surfaceflinger appdomain:binder transfer;
-#line 49
-# Receive and use open files from the server.
-#line 49
-allow appdomain surfaceflinger:fd use;
-#line 49
-
-
-# App sandbox file accesses.
-allow appdomain app_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow appdomain app_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read/write data files created by the platform apps if they
-# were passed to the app via binder or local IPC.  Do not allow open.
-allow appdomain platform_app_data_file:file { getattr read write };
-
-# lib subdirectory of /data/data dir is system-owned.
-allow appdomain system_data_file:dir { open getattr read search ioctl };
-allow appdomain system_data_file:file { execute execute_no_trans open };
-
-# Execute the shell or other system executables.
-allow appdomain shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow appdomain system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# Read/write wallpaper file (opened by system).
-allow appdomain wallpaper_file:file { getattr read write };
-
-# Write to /data/anr/traces.txt.
-allow appdomain anr_data_file:dir search;
-allow appdomain anr_data_file:file { open append };
-
-# Allow apps to send dump information to dumpstate
-allow appdomain dumpstate:fd use;
-allow appdomain dumpstate:unix_stream_socket { read write getopt getattr };
-allow appdomain shell_data_file:file { write getattr };
-
-# Write to /proc/net/xt_qtaguid/ctrl file.
-allow appdomain qtaguid_proc:file { { getattr open read ioctl lock } { open append write } };
-# Everybody can read the xt_qtaguid resource tracking misc dev.
-# So allow all apps to read from /dev/xt_qtaguid.
-allow appdomain qtaguid_device:chr_file { getattr open read ioctl lock };
-
-# Grant GPU access to all processes started by Zygote.
-# They need that to render the standard UI.
-allow appdomain gpu_device:chr_file { { { getattr open read ioctl lock } { open append write } } execute };
-
-# Use the Binder.
-
-#line 90
-# Call the servicemanager and transfer references to it.
-#line 90
-allow appdomain servicemanager:binder { call transfer };
-#line 90
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 90
-# all domains in domain.te.
-#line 90
-
-# Perform binder IPC to binder services.
-
-#line 92
-# Call the server domain and optionally transfer references to it.
-#line 92
-allow appdomain binderservicedomain:binder { call transfer };
-#line 92
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 92
-allow binderservicedomain appdomain:binder transfer;
-#line 92
-# Receive and use open files from the server.
-#line 92
-allow appdomain binderservicedomain:fd use;
-#line 92
-
-# Perform binder IPC to other apps.
-
-#line 94
-# Call the server domain and optionally transfer references to it.
-#line 94
-allow appdomain appdomain:binder { call transfer };
-#line 94
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 94
-allow appdomain appdomain:binder transfer;
-#line 94
-# Receive and use open files from the server.
-#line 94
-allow appdomain appdomain:fd use;
-#line 94
-
-
-# Appdomain interaction with isolated apps
-
-#line 97
-allow appdomain isolated_app:dir { open getattr read search ioctl };
-#line 97
-allow appdomain isolated_app:{ file lnk_file } { getattr open read ioctl lock };
-#line 97
-
-
-# Already connected, unnamed sockets being passed over some other IPC
-# hence no sock_file or connectto permission. This appears to be how
-# Chrome works, may need to be updated as more apps using isolated services
-# are examined.
-allow appdomain isolated_app:unix_stream_socket { read write };
-
-# Backup ability for every app. BMS opens and passes the fd
-# to any app that has backup ability. Hence, no open permissions here.
-allow appdomain backup_data_file:file { read write getattr };
-allow appdomain cache_backup_file:file { read write getattr };
-# Backup ability using 'adb backup'
-allow appdomain system_data_file:lnk_file getattr;
-
-# Allow all applications to read downloaded files
-allow appdomain download_file:dir search;
-allow appdomain download_file:file { getattr open read ioctl lock };
-
-# Allow applications to communicate with netd via /dev/socket/dnsproxyd
-# to do DNS resolution
-
-#line 118
-allow appdomain dnsproxyd_socket:sock_file write;
-#line 118
-allow appdomain netd:unix_stream_socket connectto;
-#line 118
-
-
-# Allow applications to communicate with drmserver over binder
-
-#line 121
-# Call the server domain and optionally transfer references to it.
-#line 121
-allow appdomain drmserver:binder { call transfer };
-#line 121
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 121
-allow drmserver appdomain:binder transfer;
-#line 121
-# Receive and use open files from the server.
-#line 121
-allow appdomain drmserver:fd use;
-#line 121
-
-
-# Allow applications to communicate with mediaserver over binder
-
-#line 124
-# Call the server domain and optionally transfer references to it.
-#line 124
-allow appdomain mediaserver:binder { call transfer };
-#line 124
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 124
-allow mediaserver appdomain:binder transfer;
-#line 124
-# Receive and use open files from the server.
-#line 124
-allow appdomain mediaserver:fd use;
-#line 124
-
-
-# Allow applications to make outbound tcp connections to any port
-allow appdomain port_type:tcp_socket name_connect;
-
-# Allow apps to see changes to the routing table.
-allow appdomain self:netlink_route_socket {
-    read
-    bind
-    create
-    nlmsg_read
-    ioctl
-    getattr
-    setattr
-    getopt
-    setopt
-    shutdown
-};
-
-# Allow apps to use rawip sockets. This is needed for apps which execute
-# /system/bin/ping, for example.
-allow appdomain self:rawip_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-
-# Allow apps to use the USB Accessory interface.
-# http://developer.android.com/guide/topics/connectivity/usb/accessory.html
-#
-# USB devices are first opened by the system server (USBDeviceManagerService)
-# and the file descriptor is passed to the right Activity via binder.
-allow appdomain usb_device:chr_file { read write getattr ioctl };
-allow appdomain usbaccessory_device:chr_file { read write getattr };
-
-# For art.
-allow appdomain dalvikcache_data_file:file execute;
-
-# For legacy unlabeled userdata on existing devices.
-# See discussion of Unlabeled files in domain.te for more information.
-allow appdomain unlabeled:file { getattr execute execute_no_trans };
-
-###
-### CTS-specific rules
-###
-
-# For cts/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/RootProcessScanner.java.
-# Reads /proc/pid/status and statm entries to check that
-# no unexpected root processes are running.
-# Also for cts/tests/tests/security/src/android/security/cts/VoldExploitTest.java
-# Reads /proc/pid/cmdline of vold.
-allow appdomain domain:dir { open read search getattr };
-allow appdomain domain:{ file lnk_file } { open read getattr };
-
-# For cts/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java.
-# testRunAsHasCorrectCapabilities
-allow appdomain runas_exec:file getattr;
-# Others are either allowed elsewhere or not desired.
-
-# For cts/tests/tests/security/src/android/security/cts/SELinuxTest.java
-# Check SELinux policy and contexts.
-
-#line 181
-allow appdomain selinuxfs:dir { open getattr read search ioctl };
-#line 181
-allow appdomain selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 181
-allow appdomain kernel:security compute_av;
-#line 181
-allow appdomain self:netlink_selinux_socket *;
-#line 181
-
-
-#line 182
-allow appdomain selinuxfs:dir { open getattr read search ioctl };
-#line 182
-allow appdomain selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 182
-allow appdomain kernel:security check_context;
-#line 182
-
-# Validate that each process is running in the correct security context.
-allow appdomain domain:process getattr;
-
-# logd access
-
-#line 187
-
-#line 187
-allow appdomain logdr_socket:sock_file write;
-#line 187
-allow appdomain logd:unix_stream_socket connectto;
-#line 187
-
-#line 187
-
-# application inherit logd write socket (urge is to deprecate this long term)
-allow appdomain zygote:unix_dgram_socket write;
-
-###
-### Neverallow rules
-###
-### These are things that Android apps should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-neverallow { appdomain -unconfineddomain } self:capability2 *;
-
-# Block device access.
-neverallow { appdomain -unconfineddomain } dev_type:blk_file { read write };
-
-# Access to any of the following character devices.
-neverallow { appdomain -unconfineddomain } {
-    audio_device
-    camera_device
-    dm_device
-    radio_device
-    gps_device
-    rpmsg_device
-}:chr_file { read write };
-
-# Note: Try expanding list of app domains in the future.
-neverallow { untrusted_app isolated_app shell -unconfineddomain }
-    graphics_device:chr_file { read write };
-
-neverallow { appdomain -nfc -unconfineddomain } nfc_device:chr_file
-    { read write };
-neverallow { appdomain -bluetooth -unconfineddomain } hci_attach_dev:chr_file
-    { read write };
-neverallow { appdomain -unconfineddomain } tee_device:chr_file { read write };
-
-# Set SELinux enforcing mode, booleans or any other SELinux settings.
-neverallow { appdomain -unconfineddomain } kernel:security
-    { setenforce setbool setsecparam setcheckreqprot };
-
-# Load security policy.
-neverallow appdomain kernel:security load_policy;
-
-# Privileged netlink socket interfaces.
-neverallow { appdomain -unconfineddomain }
-    self:{
-        netlink_socket
-        netlink_firewall_socket
-        netlink_tcpdiag_socket
-        netlink_nflog_socket
-        netlink_xfrm_socket
-        netlink_audit_socket
-        netlink_ip6fw_socket
-        netlink_dnrt_socket
-        netlink_kobject_uevent_socket
-    } *;
-
-# Sockets under /dev/socket that are not specifically typed.
-neverallow { appdomain -unconfineddomain } socket_device:sock_file write;
-
-# Unix domain sockets.
-neverallow { appdomain -unconfineddomain } adbd_socket:sock_file write;
-neverallow { appdomain -unconfineddomain } installd_socket:sock_file write;
-neverallow { appdomain -bluetooth -radio -shell -system_app -unconfineddomain }
-    property_socket:sock_file write;
-neverallow { appdomain -radio -unconfineddomain } rild_socket:sock_file write;
-neverallow { appdomain -unconfineddomain } vold_socket:sock_file write;
-neverallow { appdomain -unconfineddomain } zygote_socket:sock_file write;
-
-# ptrace access to non-app domains.
-neverallow { appdomain -unconfineddomain } { domain -appdomain }:process ptrace;
-
-# Write access to /proc/pid entries for any non-app domain.
-neverallow { appdomain -unconfineddomain } { domain -appdomain }:file write;
-
-# signal access to non-app domains.
-# sigchld allowed for parent death notification.
-# signull allowed for kill(pid, 0) existence test.
-# All others prohibited.
-neverallow { appdomain -unconfineddomain } { domain -appdomain }:process
-    { sigkill sigstop signal };
-
-# Transition to a non-app domain.
-# Exception for the shell domain, can transition to runas, etc.
-neverallow { appdomain -shell -unconfineddomain } ~appdomain:process
-    { transition dyntransition };
-
-# Map low memory.
-# Note: Take to domain.te and apply to all domains in the future.
-neverallow { appdomain -unconfineddomain } self:memprotect mmap_zero;
-
-# Write to rootfs.
-neverallow { appdomain -unconfineddomain } rootfs:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to /system.
-neverallow { appdomain -unconfineddomain } system_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to entrypoint executables.
-neverallow { appdomain -unconfineddomain } exec_type:file
-    { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to system-owned parts of /data.
-# This is the default type for anything under /data not otherwise
-# specified in file_contexts.  Define a different type for portions
-# that should be writable by apps.
-# Exception for system_app for Settings.
-neverallow { appdomain -unconfineddomain -system_app }
-    system_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to various other parts of /data.
-neverallow { appdomain -system_app -unconfineddomain }
-    security_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain } drm_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain } gps_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
-    apk_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
-    apk_tmp_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
-    apk_private_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
-    apk_private_tmp_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -shell -unconfineddomain }
-    shell_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -bluetooth -unconfineddomain }
-    bluetooth_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
-    keystore_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
-    systemkeys_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
-    wifi_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
-    dhcp_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
-    { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Access to factory files.
-neverallow { appdomain -unconfineddomain }
-    efs_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } { read write };
-
-# Write to various pseudo file systems.
-neverallow { appdomain -bluetooth -nfc -unconfineddomain }
-    sysfs:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-neverallow { appdomain -unconfineddomain }
-    proc:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-
-# Access to syslog(2) or /proc/kmsg.
-neverallow { appdomain -system_app -unconfineddomain }
-    kernel:system { syslog_read syslog_mod syslog_console };
-
-# Ability to perform any filesystem operation other than statfs(2).
-# i.e. no mount(2), unmount(2), etc.
-neverallow { appdomain -unconfineddomain } fs_type:filesystem ~getattr;
-
-# Ability to set system properties.
-neverallow { appdomain -system_app -radio -shell -bluetooth -unconfineddomain }
-    property_type:property_service set;
-#line 1 "external/sepolicy/binderservicedomain.te"
-# Rules common to all binder service domains
-
-# Allow dumpstate to collect information from binder services
-allow binderservicedomain dumpstate:fd use;
-allow binderservicedomain dumpstate:unix_stream_socket { read write getopt getattr };
-allow binderservicedomain shell_data_file:file { getattr write };
-
-# Allow dumpsys to work from adb shell
-allow binderservicedomain devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/bluetooth.te"
-# bluetooth subsystem
-type bluetooth, domain;
-
-#line 3
-typeattribute bluetooth appdomain;
-#line 3
-# Label ashmem objects with our own unique type.
-#line 3
-
-#line 3
-type bluetooth_tmpfs, file_type;
-#line 3
-type_transition bluetooth tmpfs:file bluetooth_tmpfs;
-#line 3
-allow bluetooth bluetooth_tmpfs:file { read write };
-#line 3
-
-#line 3
-# Map with PROT_EXEC.
-#line 3
-allow bluetooth bluetooth_tmpfs:file execute;
-#line 3
-
-
-# Data file accesses.
-allow bluetooth bluetooth_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow bluetooth bluetooth_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Socket creation under /data/misc/bluedroid.
-type_transition bluetooth bluetooth_data_file:sock_file bluetooth_socket;
-allow bluetooth bluetooth_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# bluetooth factory file accesses.
-
-#line 14
-allow bluetooth bluetooth_efs_file:dir { open getattr read search ioctl };
-#line 14
-allow bluetooth bluetooth_efs_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 14
-
-
-# Device accesses.
-allow bluetooth { tun_device uhid_device hci_attach_dev }:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Other domains that can create and use bluetooth sockets.
-# SELinux does not presently define a specific socket class for
-# bluetooth sockets, nor does it distinguish among the bluetooth protocols.
-allow bluetoothdomain self:socket *;
-
-# sysfs access.
-allow bluetooth sysfs_bluetooth_writable:file { { getattr open read ioctl lock } { open append write } };
-allow bluetooth self:capability net_admin;
-
-# Allow clients to use a socket provided by the bluetooth app.
-allow bluetoothdomain bluetooth:unix_stream_socket { read write shutdown };
-
-# tethering
-allow bluetooth self:{ tun_socket udp_socket } { ioctl create };
-allow bluetooth efs_file:dir search;
-
-# Talk to init over the property socket.
-
-#line 36
-allow bluetooth property_socket:sock_file write;
-#line 36
-allow bluetooth init:unix_stream_socket connectto;
-#line 36
-
-
-# proc access.
-allow bluetooth proc_bluetooth_writable:file { { getattr open read ioctl lock } { open append write } };
-
-# bluetooth file transfers
-allow bluetooth sdcard_internal:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow bluetooth sdcard_internal:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Allow reading of media_rw_data_file file descriptors
-# passed to bluetooth
-allow bluetooth media_rw_data_file:file { read getattr };
-
-# Allow write access to bluetooth specific properties
-allow bluetooth bluetooth_prop:property_service set;
-
-###
-### Neverallow rules
-###
-### These are things that the bluetooth app should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { bluetooth -unconfineddomain } self:capability ~net_admin;
-#line 1 "external/sepolicy/bootanim.te"
-# bootanimation oneshot service
-type bootanim, domain;
-type bootanim_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init bootanim_exec:file { getattr open read execute };
-#line 5
-allow init bootanim:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow bootanim bootanim_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow bootanim init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init bootanim:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init bootanim:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init bootanim_exec:process bootanim;
-#line 5
-
-#line 5
-
-#line 5
-type bootanim_tmpfs, file_type;
-#line 5
-type_transition bootanim tmpfs:file bootanim_tmpfs;
-#line 5
-allow bootanim bootanim_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-
-#line 7
-# Call the servicemanager and transfer references to it.
-#line 7
-allow bootanim servicemanager:binder { call transfer };
-#line 7
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 7
-# all domains in domain.te.
-#line 7
-
-
-#line 8
-# Call the server domain and optionally transfer references to it.
-#line 8
-allow bootanim surfaceflinger:binder { call transfer };
-#line 8
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 8
-allow surfaceflinger bootanim:binder transfer;
-#line 8
-# Receive and use open files from the server.
-#line 8
-allow bootanim surfaceflinger:fd use;
-#line 8
-
-
-allow bootanim gpu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/clatd.te"
-# 464xlat daemon
-type clatd, domain;
-
-#line 3
-typeattribute clatd mlstrustedsubject;
-#line 3
-typeattribute clatd unconfineddomain;
-#line 3
-
-type clatd_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init clatd_exec:file { getattr open read execute };
-#line 6
-allow init clatd:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow clatd clatd_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow clatd init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init clatd:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init clatd:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init clatd_exec:process clatd;
-#line 6
-
-#line 6
-
-#line 6
-type clatd_tmpfs, file_type;
-#line 6
-type_transition clatd tmpfs:file clatd_tmpfs;
-#line 6
-allow clatd clatd_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute clatd netdomain;
-#line 7
-
-#line 1 "external/sepolicy/debuggerd.te"
-# debugger interface
-type debuggerd, domain;
-type debuggerd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init debuggerd_exec:file { getattr open read execute };
-#line 5
-allow init debuggerd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow debuggerd debuggerd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow debuggerd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init debuggerd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init debuggerd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init debuggerd_exec:process debuggerd;
-#line 5
-
-#line 5
-
-#line 5
-type debuggerd_tmpfs, file_type;
-#line 5
-type_transition debuggerd tmpfs:file debuggerd_tmpfs;
-#line 5
-allow debuggerd debuggerd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute debuggerd mlstrustedsubject;
-allow debuggerd self:capability { dac_override sys_ptrace chown kill fowner };
-allow debuggerd self:capability2 { syslog };
-allow debuggerd domain:dir { open getattr read search ioctl };
-allow debuggerd domain:file { getattr open read ioctl lock };
-allow debuggerd { domain -init -ueventd -watchdogd -healthd -adbd }:process ptrace;
-
-#line 12
-allow debuggerd security_file:dir { open getattr read search ioctl };
-#line 12
-allow debuggerd security_file:file { getattr open read ioctl lock };
-#line 12
-allow debuggerd security_file:lnk_file { getattr open read ioctl lock };
-#line 12
-allow debuggerd selinuxfs:dir { open getattr read search ioctl };
-#line 12
-allow debuggerd selinuxfs:file { getattr open read ioctl lock };
-#line 12
-allow debuggerd rootfs:dir { open getattr read search ioctl };
-#line 12
-allow debuggerd rootfs:file { getattr open read ioctl lock };
-#line 12
-
-allow debuggerd system_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow debuggerd system_data_file:dir relabelfrom;
-
-#line 15
-typeattribute debuggerd relabeltodomain;
-#line 15
-
-allow debuggerd tombstone_data_file:dir relabelto;
-allow debuggerd tombstone_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow debuggerd tombstone_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow debuggerd domain:process { sigstop signal };
-allow debuggerd exec_type:file { getattr open read ioctl lock };
-# Access app library
-allow debuggerd system_data_file:file open;
-
-# Connect to system_server via /data/system/ndebugsocket.
-
-#line 25
-allow debuggerd system_ndebug_socket:sock_file write;
-#line 25
-allow debuggerd system_server:unix_stream_socket connectto;
-#line 25
-
-
-#line 30
-
-
-# logd access
-
-#line 33
-
-#line 33
-allow debuggerd logdr_socket:sock_file write;
-#line 33
-allow debuggerd logd:unix_stream_socket connectto;
-#line 33
-
-#line 33
-
-#line 1 "external/sepolicy/device.te"
-# Device types
-type device, dev_type, fs_type;
-type alarm_device, dev_type, mlstrustedobject;
-type adb_device, dev_type;
-type ashmem_device, dev_type, mlstrustedobject;
-type audio_device, dev_type;
-type binder_device, dev_type, mlstrustedobject;
-type block_device, dev_type;
-type camera_device, dev_type;
-type dm_device, dev_type;
-type loop_device, dev_type;
-type radio_device, dev_type;
-type ram_device, dev_type;
-type console_device, dev_type;
-type cpuctl_device, dev_type;
-type fscklogs, dev_type;
-type full_device, dev_type;
-# GPU (used by most UI apps)
-type gpu_device, dev_type, mlstrustedobject;
-type graphics_device, dev_type;
-type hw_random_device, dev_type;
-type input_device, dev_type;
-type kmem_device, dev_type;
-type log_device, dev_type, mlstrustedobject;
-type mtd_device, dev_type;
-type mtp_device, dev_type, mlstrustedobject;
-type nfc_device, dev_type;
-type ptmx_device, dev_type, mlstrustedobject;
-type qemu_device, dev_type;
-type kmsg_device, dev_type;
-type null_device, dev_type, mlstrustedobject;
-type random_device, dev_type;
-type sensors_device, dev_type;
-type serial_device, dev_type;
-type socket_device, dev_type;
-type owntty_device, dev_type, mlstrustedobject;
-type tty_device, dev_type;
-type urandom_device, dev_type;
-type video_device, dev_type;
-type vcs_device, dev_type;
-type zero_device, dev_type;
-type fuse_device, dev_type;
-type iio_device, dev_type;
-type ion_device, dev_type, mlstrustedobject;
-type gps_device, dev_type;
-type qtaguid_device, dev_type;
-type watchdog_device, dev_type;
-type uhid_device, dev_type;
-type tun_device, dev_type, mlstrustedobject;
-type usbaccessory_device, dev_type;
-type usb_device, dev_type;
-type klog_device, dev_type;
-type properties_device, dev_type;
-
-# All devices have a uart for the hci
-# attach service. The uart dev node
-# varies per device. This type
-# is used in per device policy
-type hci_attach_dev, dev_type;
-
-# All devices have a rpmsg device for
-# achieving remoteproc and rpmsg modules
-type rpmsg_device, dev_type;
-
-# Partition layout block device
-type root_block_device, dev_type;
-#line 1 "external/sepolicy/dhcp.te"
-type dhcp, domain;
-
-#line 2
-typeattribute dhcp mlstrustedsubject;
-#line 2
-typeattribute dhcp unconfineddomain;
-#line 2
-
-type dhcp_exec, exec_type, file_type;
-type dhcp_data_file, file_type, data_file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init dhcp_exec:file { getattr open read execute };
-#line 6
-allow init dhcp:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow dhcp dhcp_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow dhcp init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init dhcp:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init dhcp:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init dhcp_exec:process dhcp;
-#line 6
-
-#line 6
-
-#line 6
-type dhcp_tmpfs, file_type;
-#line 6
-type_transition dhcp tmpfs:file dhcp_tmpfs;
-#line 6
-allow dhcp dhcp_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute dhcp netdomain;
-#line 7
-
-
-allow dhcp cgroup:dir { create write add_name };
-allow dhcp self:capability { setgid setuid net_admin net_raw net_bind_service };
-allow dhcp self:packet_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow dhcp self:netlink_route_socket { { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } } nlmsg_write };
-allow dhcp self:rawip_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow dhcp shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow dhcp system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-# For /proc/sys/net/ipv4/conf/*/promote_secondaries
-allow dhcp proc_net:file write;
-allow dhcp system_prop:property_service set ;
-
-#line 19
-allow dhcp property_socket:sock_file write;
-#line 19
-allow dhcp init:unix_stream_socket connectto;
-#line 19
-
-allow dhcp owntty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-type_transition dhcp system_data_file:{ dir file } dhcp_data_file;
-allow dhcp dhcp_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow dhcp dhcp_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# PAN connections
-allow dhcp netd:fd use;
-allow dhcp netd:fifo_file { { getattr open read ioctl lock } { open append write } };
-allow dhcp netd:{ { udp_socket unix_dgram_socket } unix_stream_socket } { read write };
-allow dhcp netd:{ netlink_kobject_uevent_socket netlink_route_socket netlink_nflog_socket } { read write };
-#line 1 "external/sepolicy/dnsmasq.te"
-# DNS, DHCP services
-type dnsmasq, domain;
-
-#line 3
-typeattribute dnsmasq mlstrustedsubject;
-#line 3
-typeattribute dnsmasq unconfineddomain;
-#line 3
-
-type dnsmasq_exec, exec_type, file_type;
-
-allow dnsmasq self:capability { net_bind_service setgid setuid };
-allow dnsmasq self:tcp_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-
-allow dnsmasq dhcp_data_file:dir { open search write add_name remove_name };
-allow dnsmasq dhcp_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow dnsmasq port:tcp_socket name_bind;
-allow dnsmasq node:tcp_socket node_bind;
-#line 1 "external/sepolicy/domain.te"
-# Rules for all domains.
-
-# Allow reaping by init.
-allow domain init:process sigchld;
-
-# Read access to properties mapping.
-allow domain kernel:fd use;
-allow domain tmpfs:file { read getattr };
-
-# Search /storage/emulated tmpfs mount.
-allow domain tmpfs:dir { open getattr read search ioctl };
-
-# Intra-domain accesses.
-allow domain self:process ~{ execmem execstack execheap ptrace };
-allow domain self:fd use;
-allow domain self:dir { open getattr read search ioctl };
-allow domain self:lnk_file { getattr open read ioctl lock };
-allow domain self:{ fifo_file file } { { getattr open read ioctl lock } { open append write } };
-allow domain self:{ unix_dgram_socket unix_stream_socket } *;
-
-# Inherit or receive open files from others.
-allow domain init:fd use;
-allow domain system_server:fd use;
-
-# Connect to adbd and use a socket transferred from it.
-# This is used for e.g. adb backup/restore.
-allow domain adbd:unix_stream_socket connectto;
-allow domain adbd:fd use;
-allow domain adbd:unix_stream_socket { getattr getopt read write shutdown };
-
-#line 43
-
-
-###
-### Talk to debuggerd.
-###
-allow domain debuggerd:process sigchld;
-allow domain debuggerd:unix_stream_socket connectto;
-
-# Root fs.
-allow domain rootfs:dir { open getattr read search ioctl };
-allow domain rootfs:file { getattr open read ioctl lock };
-allow domain rootfs:lnk_file { getattr open read ioctl lock };
-
-# Device accesses.
-allow domain device:dir search;
-allow domain dev_type:lnk_file { getattr open read ioctl lock };
-allow domain devpts:dir search;
-allow domain device:file read;
-allow domain socket_device:dir search;
-allow domain owntty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain null_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain zero_device:chr_file { getattr open read ioctl lock };
-allow domain ashmem_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain binder_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain ptmx_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain log_device:dir search;
-allow domain log_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain alarm_device:chr_file { getattr open read ioctl lock };
-allow domain urandom_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain random_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain properties_device:file { getattr open read ioctl lock };
-
-# logd access
-
-#line 76
-
-#line 76
-
-#line 76
-allow domain logdw_socket:sock_file write;
-#line 76
-allow domain logd:unix_dgram_socket sendto;
-#line 76
-
-#line 76
-
-
-# Filesystem accesses.
-allow domain fs_type:filesystem getattr;
-allow domain fs_type:dir getattr;
-
-# System file accesses.
-allow domain system_file:dir { open getattr read search ioctl };
-allow domain system_file:file { getattr open read ioctl lock };
-allow domain system_file:file execute;
-allow domain system_file:lnk_file { getattr open read ioctl lock };
-
-# Read files already opened under /data.
-allow domain system_data_file:dir { search getattr };
-allow domain system_data_file:file { getattr read };
-allow domain system_data_file:lnk_file { getattr open read ioctl lock };
-
-# Read apk files under /data/app.
-allow domain apk_data_file:dir { getattr search };
-allow domain apk_data_file:file { getattr open read ioctl lock };
-
-# Read /data/dalvik-cache.
-allow domain dalvikcache_data_file:dir { search getattr };
-allow domain dalvikcache_data_file:file { getattr open read ioctl lock };
-
-# Read already opened /cache files.
-allow domain cache_file:dir { open getattr read search ioctl };
-allow domain cache_file:file { getattr read };
-allow domain cache_file:lnk_file { getattr open read ioctl lock };
-
-# Read timezone related information
-
-#line 107
-allow domain zoneinfo_data_file:dir { open getattr read search ioctl };
-#line 107
-allow domain zoneinfo_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 107
-
-
-# For /acct/uid/*/tasks.
-allow domain cgroup:dir { search write };
-allow domain cgroup:file { open append write };
-
-#Allow access to ion memory allocation device
-allow domain ion_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Read access to pseudo filesystems.
-
-#line 117
-allow domain proc:dir { open getattr read search ioctl };
-#line 117
-allow domain proc:{ file lnk_file } { getattr open read ioctl lock };
-#line 117
-
-
-#line 118
-allow domain sysfs:dir { open getattr read search ioctl };
-#line 118
-allow domain sysfs:{ file lnk_file } { getattr open read ioctl lock };
-#line 118
-
-
-#line 119
-allow domain sysfs_devices_system_cpu:dir { open getattr read search ioctl };
-#line 119
-allow domain sysfs_devices_system_cpu:{ file lnk_file } { getattr open read ioctl lock };
-#line 119
-
-
-#line 120
-allow domain inotify:dir { open getattr read search ioctl };
-#line 120
-allow domain inotify:{ file lnk_file } { getattr open read ioctl lock };
-#line 120
-
-
-#line 121
-allow domain cgroup:dir { open getattr read search ioctl };
-#line 121
-allow domain cgroup:{ file lnk_file } { getattr open read ioctl lock };
-#line 121
-
-
-#line 122
-allow domain proc_net:dir { open getattr read search ioctl };
-#line 122
-allow domain proc_net:{ file lnk_file } { getattr open read ioctl lock };
-#line 122
-
-
-# debugfs access
-allow domain debugfs:dir { open getattr read search ioctl };
-allow domain debugfs:file { open append write };
-
-# Get SELinux enforcing status.
-
-#line 129
-allow domain selinuxfs:dir { open getattr read search ioctl };
-#line 129
-allow domain selinuxfs:file { getattr open read ioctl lock };
-#line 129
-
-
-# security files
-allow domain security_file:dir { search getattr };
-allow domain security_file:file getattr;
-
-# World readable asec image contents
-allow domain asec_public_file:file { getattr open read ioctl lock };
-allow domain { asec_public_file asec_apk_file }:dir { open getattr read search ioctl };
-
-######## Backwards compatibility - Unlabeled files ############
-
-# Revert to DAC rules when looking at unlabeled files. Over time, the number
-# of unlabeled files should decrease.
-# TODO: delete these rules in the future.
-#
-# Note on relabelfrom: We allow any app relabelfrom, but without the relabelto
-# capability, it's essentially useless. This is needed to allow an app with
-# relabelto to relabel unlabeled files.
-#
-allow domain unlabeled:{ file lnk_file sock_file fifo_file } { { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } } relabelfrom };
-allow domain unlabeled:dir { { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } relabelfrom };
-neverallow { domain -relabeltodomain } *:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-
-###
-### neverallow rules
-###
-
-# Limit ability to ptrace or read sensitive /proc/pid files of processes
-# with other UIDs to these whitelisted domains.
-neverallow { domain -debuggerd -vold -dumpstate -system_server } self:capability sys_ptrace;
-
-# Limit device node creation and raw I/O to these whitelisted domains.
-neverallow { domain -kernel -init -recovery -ueventd -watchdogd -healthd -vold -uncrypt } self:capability { sys_rawio mknod };
-
-# No domain needs mac_override as it is unused by SELinux.
-neverallow domain self:capability2 mac_override;
-
-# Only recovery needs mac_admin to set contexts not defined in current policy.
-neverallow { domain -recovery } self:capability2 mac_admin;
-
-# Only init should be able to load SELinux policies.
-# The first load technically occurs while still in the kernel domain,
-# but this does not trigger a denial since there is no policy yet.
-# Policy reload requires allowing this to the init domain.
-neverallow { domain -init } kernel:security load_policy;
-
-# Only init prior to switching context should be able to set enforcing mode.
-# init starts in kernel domain and switches to init domain via setcon in
-# the init.rc, so the setenforce occurs while still in kernel. After
-# switching domains, there is never any need to setenforce again by init.
-neverallow { domain -kernel } kernel:security { setenforce setcheckreqprot };
-
-# Only init, ueventd and system_server should be able to access HW RNG
-neverallow { domain -init -system_server -ueventd -unconfineddomain } hw_random_device:chr_file *;
-
-# Ensure that all entrypoint executables are in exec_type.
-neverallow domain { file_type -exec_type }:file entrypoint;
-
-# Ensure that nothing in userspace can access /dev/mem or /dev/kmem
-neverallow { domain -kernel -ueventd -init } kmem_device:chr_file *;
-neverallow domain kmem_device:chr_file ~{ create relabelto unlink setattr };
-
-# Only init should be able to configure kernel usermodehelpers or
-# security-sensitive proc settings.
-neverallow { domain -init } usermodehelper:file { append write };
-neverallow { domain -init } proc_security:file { append write };
-
-# No domain should be allowed to ptrace init.
-neverallow domain init:process ptrace;
-
-# Init can't receive binder calls. If this neverallow rule is being
-# triggered, it's probably due to a service with no SELinux domain.
-neverallow domain init:binder call;
-
-# Don't allow raw read/write/open access to block_device
-# Rather force a relabel to a more specific type
-neverallow { domain -kernel -init -recovery -vold -uncrypt } block_device:blk_file { open read write };
-
-# Don't allow raw read/write/open access to generic devices.
-# Rather force a relabel to a more specific type.
-# ueventd is exempt from this, as its managing these devices.
-neverallow { domain -unconfineddomain -ueventd } device:chr_file { open read write };
-
-# Limit what domains can mount filesystems or change their mount flags.
-# sdcard_type / vfat is exempt as a larger set of domains need
-# this capability, including device-specific domains.
-neverallow { domain -kernel -init -recovery -vold -zygote } { fs_type -sdcard_type }:filesystem { mount remount relabelfrom relabelto };
-#line 1 "external/sepolicy/drmserver.te"
-# drmserver - DRM service
-type drmserver, domain;
-type drmserver_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init drmserver_exec:file { getattr open read execute };
-#line 5
-allow init drmserver:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow drmserver drmserver_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow drmserver init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init drmserver:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init drmserver:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init drmserver_exec:process drmserver;
-#line 5
-
-#line 5
-
-#line 5
-type drmserver_tmpfs, file_type;
-#line 5
-type_transition drmserver tmpfs:file drmserver_tmpfs;
-#line 5
-allow drmserver drmserver_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute drmserver mlstrustedsubject;
-
-# Perform Binder IPC to system server.
-
-#line 9
-# Call the servicemanager and transfer references to it.
-#line 9
-allow drmserver servicemanager:binder { call transfer };
-#line 9
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 9
-# all domains in domain.te.
-#line 9
-
-
-#line 10
-# Call the server domain and optionally transfer references to it.
-#line 10
-allow drmserver system_server:binder { call transfer };
-#line 10
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 10
-allow system_server drmserver:binder transfer;
-#line 10
-# Receive and use open files from the server.
-#line 10
-allow drmserver system_server:fd use;
-#line 10
-
-
-#line 11
-# Call the server domain and optionally transfer references to it.
-#line 11
-allow drmserver appdomain:binder { call transfer };
-#line 11
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 11
-allow appdomain drmserver:binder transfer;
-#line 11
-# Receive and use open files from the server.
-#line 11
-allow drmserver appdomain:fd use;
-#line 11
-
-
-#line 12
-typeattribute drmserver binderservicedomain;
-#line 12
-
-
-# Perform Binder IPC to mediaserver
-
-#line 15
-# Call the server domain and optionally transfer references to it.
-#line 15
-allow drmserver mediaserver:binder { call transfer };
-#line 15
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 15
-allow mediaserver drmserver:binder transfer;
-#line 15
-# Receive and use open files from the server.
-#line 15
-allow drmserver mediaserver:fd use;
-#line 15
-
-
-allow drmserver sdcard_type:dir search;
-allow drmserver drm_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow drmserver drm_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow drmserver self:{ tcp_socket udp_socket } *;
-allow drmserver port:tcp_socket name_connect;
-allow drmserver tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow drmserver platform_app_data_file:file { read write getattr };
-allow drmserver app_data_file:file { read write getattr };
-allow drmserver sdcard_type:file { read write getattr };
-
-#line 26
-allow drmserver efs_file:dir { open getattr read search ioctl };
-#line 26
-allow drmserver efs_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 26
-
-
-type drmserver_socket, file_type;
-
-# /data/app/tlcd_sock socket file.
-# Clearly, /data/app is the most logical place to create a socket.  Not.
-allow drmserver apk_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-type_transition drmserver apk_data_file:sock_file drmserver_socket;
-allow drmserver drmserver_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow drmserver tee:unix_stream_socket connectto;
-# Delete old socket file if present.
-allow drmserver apk_data_file:sock_file unlink;
-
-# After taking a video, drmserver looks at the video file.
-
-#line 40
-allow drmserver media_rw_data_file:dir { open getattr read search ioctl };
-#line 40
-allow drmserver media_rw_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 40
-
-#line 1 "external/sepolicy/dumpstate.te"
-# dumpstate
-type dumpstate, domain;
-
-#line 3
-typeattribute dumpstate mlstrustedsubject;
-#line 3
-typeattribute dumpstate unconfineddomain;
-#line 3
-
-type dumpstate_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init dumpstate_exec:file { getattr open read execute };
-#line 6
-allow init dumpstate:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow dumpstate dumpstate_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow dumpstate init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init dumpstate:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init dumpstate:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init dumpstate_exec:process dumpstate;
-#line 6
-
-#line 6
-
-#line 6
-type dumpstate_tmpfs, file_type;
-#line 6
-type_transition dumpstate tmpfs:file dumpstate_tmpfs;
-#line 6
-allow dumpstate dumpstate_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute dumpstate netdomain;
-#line 7
-
-
-#line 8
-typeattribute dumpstate relabeltodomain;
-#line 8
-
-
-#line 9
-# Call the servicemanager and transfer references to it.
-#line 9
-allow dumpstate servicemanager:binder { call transfer };
-#line 9
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 9
-# all domains in domain.te.
-#line 9
-
-
-# Drop privileges by switching UID / GID
-allow dumpstate self:capability { setuid setgid };
-
-# Allow dumpstate to scan through /proc/pid for all processes
-
-#line 15
-allow dumpstate domain:dir { open getattr read search ioctl };
-#line 15
-allow dumpstate domain:{ file lnk_file } { getattr open read ioctl lock };
-#line 15
-
-
-# Send signals to processes
-allow dumpstate self:capability kill;
-
-# Allow executing files on system, such as:
-#   /system/bin/toolbox
-#   /system/bin/logcat
-#   /system/bin/dumpsys
-allow dumpstate system_file:file execute_no_trans;
-
-# Create and write into /data/anr/
-allow dumpstate self:capability { dac_override chown fowner fsetid };
-allow dumpstate anr_data_file:dir { { { open getattr read search ioctl } { open search write add_name remove_name } } relabelto };
-allow dumpstate anr_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow dumpstate system_data_file:dir { { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } relabelfrom };
-
-# Allow reading /data/system/uiderrors.txt
-# TODO: scope this down.
-allow dumpstate system_data_file:file { getattr open read ioctl lock };
-
-# Read dmesg
-allow dumpstate self:capability2 syslog;
-allow dumpstate kernel:system syslog_read;
-
-# Get process attributes
-allow dumpstate domain:process getattr;
-
-# Signal java processes to dump their stack
-allow dumpstate { appdomain system_server }:process signal;
-
-# Signal native processes to dump their stack.
-# This list comes from native_processes_to_dump in dumpstate/utils.c
-allow dumpstate { drmserver mediaserver sdcardd surfaceflinger }:process signal;
-
-# The /system/bin/ip command needs this for routing table information.
-allow dumpstate self:netlink_route_socket { write getattr setopt };
-
-# The vdc command needs to talk to the vold socket.
-
-#line 54
-allow dumpstate vold_socket:sock_file write;
-#line 54
-allow dumpstate vold:unix_stream_socket connectto;
-#line 54
-
-
-# Vibrate the device after we're done collecting the bugreport
-# /sys/class/timed_output/vibrator/enable
-# TODO: create a new file class, instead of allowing write access to all of /sys
-allow dumpstate sysfs:file { open append write };
-
-# Other random bits of data we want to collect
-allow dumpstate qtaguid_proc:file { getattr open read ioctl lock };
-allow dumpstate debugfs:file { getattr open read ioctl lock };
-
-# Allow dumpstate to make binder calls to any binder service
-
-#line 66
-# Call the server domain and optionally transfer references to it.
-#line 66
-allow dumpstate binderservicedomain:binder { call transfer };
-#line 66
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 66
-allow binderservicedomain dumpstate:binder transfer;
-#line 66
-# Receive and use open files from the server.
-#line 66
-allow dumpstate binderservicedomain:fd use;
-#line 66
-
-
-#line 67
-# Call the server domain and optionally transfer references to it.
-#line 67
-allow dumpstate appdomain:binder { call transfer };
-#line 67
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 67
-allow appdomain dumpstate:binder transfer;
-#line 67
-# Receive and use open files from the server.
-#line 67
-allow dumpstate appdomain:fd use;
-#line 67
-
-
-# Reading /proc/PID/maps of other processes
-allow dumpstate self:capability sys_ptrace;
-
-# Allow the bugreport service to create a file in
-# /data/data/com.android.shell/files/bugreports/bugreport
-allow dumpstate shell_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow dumpstate shell_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Run a shell.
-allow dumpstate shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# For running am and similar framework commands.
-# Run /system/bin/app_process.
-allow dumpstate zygote_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-# Dalvik Compiler JIT.
-allow dumpstate ashmem_device:chr_file execute;
-allow dumpstate dumpstate_tmpfs:file execute;
-allow dumpstate self:process execmem;
-# For art.
-allow dumpstate dalvikcache_data_file:file execute;
-
-# logd access
-
-#line 91
-
-#line 91
-allow dumpstate logdr_socket:sock_file write;
-#line 91
-allow dumpstate logd:unix_stream_socket connectto;
-#line 91
-
-#line 91
-
-
-#line 92
-# Group AID_LOG checked by filesystem & logd
-#line 92
-# to permit control commands
-#line 92
-
-#line 92
-allow dumpstate logd_socket:sock_file write;
-#line 92
-allow dumpstate logd:unix_stream_socket connectto;
-#line 92
-
-#line 92
-
-#line 1 "external/sepolicy/file.te"
-# Filesystem types
-type labeledfs, fs_type;
-type pipefs, fs_type;
-type sockfs, fs_type;
-type rootfs, fs_type;
-type proc, fs_type;
-# Security-sensitive proc nodes that should not be writable to most.
-type proc_security, fs_type;
-# proc, sysfs, or other nodes that permit configuration of kernel usermodehelpers.
-type usermodehelper, fs_type, sysfs_type;
-type qtaguid_proc, fs_type, mlstrustedobject;
-type proc_bluetooth_writable, fs_type;
-type proc_net, fs_type;
-type selinuxfs, fs_type;
-type cgroup, fs_type, mlstrustedobject;
-type sysfs, fs_type, mlstrustedobject;
-type sysfs_writable, fs_type, sysfs_type, mlstrustedobject;
-type sysfs_bluetooth_writable, fs_type, sysfs_type, mlstrustedobject;
-type sysfs_nfc_power_writable, fs_type, sysfs_type, mlstrustedobject;
-type sysfs_wake_lock, fs_type, sysfs_type;
-# /sys/devices/system/cpu
-type sysfs_devices_system_cpu, fs_type, sysfs_type;
-# /sys/module/lowmemorykiller
-type sysfs_lowmemorykiller, fs_type, sysfs_type;
-type inotify, fs_type, mlstrustedobject;
-type devpts, fs_type, mlstrustedobject;
-type tmpfs, fs_type;
-type shm, fs_type;
-type mqueue, fs_type;
-type sdcard_internal, sdcard_type, fs_type, mlstrustedobject;
-type sdcard_external, sdcard_type, fs_type, mlstrustedobject;
-type debugfs, fs_type, mlstrustedobject;
-
-# File types
-type unlabeled, file_type;
-# Default type for anything under /system.
-type system_file, file_type;
-# Default type for anything under /data.
-type system_data_file, file_type, data_file_type;
-# /data/drm - DRM plugin data
-type drm_data_file, file_type, data_file_type;
-# /data/anr - ANR traces
-type anr_data_file, file_type, data_file_type, mlstrustedobject;
-# /data/tombstones - core dumps
-type tombstone_data_file, file_type, data_file_type;
-# /data/app - user-installed apps
-type apk_data_file, file_type, data_file_type;
-type apk_tmp_file, file_type, data_file_type, mlstrustedobject;
-# /data/app-private - forward-locked apps
-type apk_private_data_file, file_type, data_file_type;
-type apk_private_tmp_file, file_type, data_file_type, mlstrustedobject;
-# /data/dalvik-cache
-type dalvikcache_data_file, file_type, data_file_type;
-# /data/local - writable by shell
-type shell_data_file, file_type, data_file_type;
-# /data/gps
-type gps_data_file, file_type, data_file_type;
-
-# /data/misc subdirectories
-type adb_keys_file, file_type, data_file_type;
-type audio_data_file, file_type, data_file_type;
-type bluetooth_data_file, file_type, data_file_type;
-type camera_data_file, file_type, data_file_type;
-type keystore_data_file, file_type, data_file_type;
-type media_data_file, file_type, data_file_type;
-type media_rw_data_file, file_type, data_file_type;
-type nfc_data_file, file_type, data_file_type;
-type radio_data_file, file_type, data_file_type;
-type systemkeys_data_file, file_type, data_file_type;
-type vpn_data_file, file_type, data_file_type;
-type wifi_data_file, file_type, data_file_type;
-type zoneinfo_data_file, file_type, data_file_type;
-
-# Compatibility with type names used in vanilla Android 4.3 and 4.4.
-typealias audio_data_file alias audio_firmware_file;
-# /data/data subdirectories - app sandboxes
-type app_data_file, file_type, data_file_type;
-type platform_app_data_file, file_type, data_file_type, mlstrustedobject;
-# Default type for anything under /cache
-type cache_file, file_type, mlstrustedobject;
-# Type for /cache/.*\.{data|restore} and default
-# type for anything under /cache/backup
-type cache_backup_file, file_type, mlstrustedobject;
-# Default type for anything under /efs
-type efs_file, file_type;
-# Type for wallpaper file.
-type wallpaper_file, file_type, mlstrustedobject;
-# /mnt/asec
-type asec_apk_file, file_type, data_file_type;
-# Elements of asec files (/mnt/asec) that are world readable
-type asec_public_file, file_type, data_file_type;
-# /data/app-asec
-type asec_image_file, file_type, data_file_type;
-# /data/backup and /data/secure/backup
-type backup_data_file, file_type, data_file_type, mlstrustedobject;
-# For /data/security
-type security_file, file_type;
-# All devices have bluetooth efs files. But they
-# vary per device, so this type is used in per
-# device policy
-type bluetooth_efs_file, file_type;
-# Downloaded files
-type download_file, file_type;
-
-# Socket types
-type adbd_socket, file_type;
-type bluetooth_socket, file_type;
-type dnsproxyd_socket, file_type, mlstrustedobject;
-type dumpstate_socket, file_type;
-type gps_socket, file_type;
-type installd_socket, file_type;
-type keystore_socket, file_type;
-type lmkd_socket, file_type;
-type logd_debug, file_type;
-type logd_socket, file_type;
-type logdr_socket, file_type;
-type logdw_socket, file_type;
-type mdns_socket, file_type;
-type netd_socket, file_type;
-type property_socket, file_type;
-type qemud_socket, file_type;
-type racoon_socket, file_type;
-type rild_socket, file_type;
-type rild_debug_socket, file_type;
-type system_wpa_socket, file_type;
-type system_ndebug_socket, file_type;
-type vold_socket, file_type;
-type wpa_socket, file_type;
-type zygote_socket, file_type;
-
-# UART (for GPS) control proc file
-type gps_control, file_type;
-
-# Allow files to be created in their appropriate filesystems.
-allow fs_type self:filesystem associate;
-allow sysfs_type sysfs:filesystem associate;
-allow file_type labeledfs:filesystem associate;
-allow file_type tmpfs:filesystem associate;
-allow file_type rootfs:filesystem associate;
-allow dev_type tmpfs:filesystem associate;
-#line 1 "external/sepolicy/gpsd.te"
-# gpsd - GPS daemon
-type gpsd, domain;
-
-#line 3
-typeattribute gpsd mlstrustedsubject;
-#line 3
-typeattribute gpsd unconfineddomain;
-#line 3
-
-type gpsd_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init gpsd_exec:file { getattr open read execute };
-#line 6
-allow init gpsd:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow gpsd gpsd_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow gpsd init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init gpsd:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init gpsd:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init gpsd_exec:process gpsd;
-#line 6
-
-#line 6
-
-#line 6
-type gpsd_tmpfs, file_type;
-#line 6
-type_transition gpsd tmpfs:file gpsd_tmpfs;
-#line 6
-allow gpsd gpsd_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute gpsd netdomain;
-#line 7
-
-allow gpsd gps_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow gpsd gps_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Socket is created by the daemon, not by init, and under /data/gps,
-# not under /dev/socket.
-type_transition gpsd gps_data_file:sock_file gps_socket;
-allow gpsd gps_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# XXX Label sysfs files with a specific type?
-allow gpsd sysfs:file { { getattr open read ioctl lock } { open append write } };
-
-allow gpsd gps_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Execute the shell or system commands.
-allow gpsd shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow gpsd system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-#line 1 "external/sepolicy/hci_attach.te"
-type hci_attach, domain;
-type hci_attach_exec, exec_type, file_type;
-
-
-#line 4
-
-#line 4
-# Allow the necessary permissions.
-#line 4
-
-#line 4
-# Old domain may exec the file and transition to the new domain.
-#line 4
-allow init hci_attach_exec:file { getattr open read execute };
-#line 4
-allow init hci_attach:process transition;
-#line 4
-# New domain is entered by executing the file.
-#line 4
-allow hci_attach hci_attach_exec:file { entrypoint read execute };
-#line 4
-# New domain can send SIGCHLD to its caller.
-#line 4
-allow hci_attach init:process sigchld;
-#line 4
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 4
-dontaudit init hci_attach:process noatsecure;
-#line 4
-# XXX dontaudit candidate but requires further study.
-#line 4
-allow init hci_attach:process { siginh rlimitinh };
-#line 4
-
-#line 4
-# Make the transition occur by default.
-#line 4
-type_transition init hci_attach_exec:process hci_attach;
-#line 4
-
-#line 4
-
-#line 4
-type hci_attach_tmpfs, file_type;
-#line 4
-type_transition hci_attach tmpfs:file hci_attach_tmpfs;
-#line 4
-allow hci_attach hci_attach_tmpfs:file { read write };
-#line 4
-
-#line 4
-
-
-allow hci_attach kernel:system module_request;
-allow hci_attach hci_attach_dev:chr_file { { getattr open read ioctl lock } { open append write } };
-allow hci_attach bluetooth_efs_file:dir { open getattr read search ioctl };
-allow hci_attach bluetooth_efs_file:file { getattr open read ioctl lock };
-#line 1 "external/sepolicy/healthd.te"
-# healthd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type healthd, domain;
-
-allow healthd rootfs:file { read entrypoint };
-
-#line 6
-type_transition healthd device:chr_file klog_device "__kmsg__";
-#line 6
-allow healthd klog_device:chr_file { create open write unlink };
-#line 6
-allow healthd device:dir { write add_name remove_name };
-#line 6
-
-# /dev/__null__ created by init prior to policy load,
-# open fd inherited by healthd.
-allow healthd tmpfs:chr_file { read write };
-
-allow healthd self:capability { net_admin mknod };
-allow healthd self:capability2 block_suspend;
-allow healthd self:netlink_kobject_uevent_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-
-#line 14
-# Call the servicemanager and transfer references to it.
-#line 14
-allow healthd servicemanager:binder { call transfer };
-#line 14
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 14
-# all domains in domain.te.
-#line 14
-
-
-#line 15
-typeattribute healthd binderservicedomain;
-#line 15
-
-
-#line 16
-# Call the server domain and optionally transfer references to it.
-#line 16
-allow healthd system_server:binder { call transfer };
-#line 16
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 16
-allow system_server healthd:binder transfer;
-#line 16
-# Receive and use open files from the server.
-#line 16
-allow healthd system_server:fd use;
-#line 16
-
-
-###
-### healthd: charger mode
-###
-
-allow healthd graphics_device:dir { open getattr read search ioctl };
-allow healthd graphics_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow healthd input_device:dir { open getattr read search ioctl };
-allow healthd input_device:chr_file { getattr open read ioctl lock };
-allow healthd ashmem_device:chr_file execute;
-allow healthd self:process execmem;
-#line 1 "external/sepolicy/hostapd.te"
-# userspace wifi access points
-type hostapd, domain;
-
-#line 3
-typeattribute hostapd mlstrustedsubject;
-#line 3
-typeattribute hostapd unconfineddomain;
-#line 3
-
-type hostapd_exec, exec_type, file_type;
-
-allow hostapd self:capability { net_admin net_raw setuid setgid };
-allow hostapd self:netlink_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow hostapd self:packet_socket { create write read };
-allow hostapd self:netlink_route_socket { bind create write nlmsg_write read };
-allow hostapd self:udp_socket { create ioctl };
-
-allow hostapd wifi_data_file:file { { getattr open read ioctl lock } { open append write } };
-allow hostapd wifi_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow hostapd wpa_socket:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow hostapd wpa_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow hostapd netd:fd use;
-allow hostapd netd:udp_socket { read write };
-allow hostapd netd:netlink_kobject_uevent_socket { read write };
-allow hostapd netd:netlink_nflog_socket { read write };
-allow hostapd netd:netlink_route_socket { read write };
-allow hostapd netd:unix_stream_socket { read write };
-allow hostapd netd:fifo_file { read write };
-#line 1 "external/sepolicy/init_shell.te"
-# Restricted domain for shell processes spawned by init
-type init_shell, domain, shelldomain;
-
-#line 3
-# Allow the necessary permissions.
-#line 3
-
-#line 3
-# Old domain may exec the file and transition to the new domain.
-#line 3
-allow init shell_exec:file { getattr open read execute };
-#line 3
-allow init init_shell:process transition;
-#line 3
-# New domain is entered by executing the file.
-#line 3
-allow init_shell shell_exec:file { entrypoint read execute };
-#line 3
-# New domain can send SIGCHLD to its caller.
-#line 3
-allow init_shell init:process sigchld;
-#line 3
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 3
-dontaudit init init_shell:process noatsecure;
-#line 3
-# XXX dontaudit candidate but requires further study.
-#line 3
-allow init init_shell:process { siginh rlimitinh };
-#line 3
-
-#line 3
-# Make the transition occur by default.
-#line 3
-type_transition init shell_exec:process init_shell;
-#line 3
-
-
-#line 4
-typeattribute init_shell mlstrustedsubject;
-#line 4
-typeattribute init_shell unconfineddomain;
-#line 4
-
-
-# inherits from shelldomain.te
-#line 1 "external/sepolicy/init.te"
-# init switches to init domain (via init.rc).
-type init, domain;
-# init is unconfined.
-
-#line 4
-typeattribute init mlstrustedsubject;
-#line 4
-typeattribute init unconfineddomain;
-#line 4
-
-
-#line 5
-type init_tmpfs, file_type;
-#line 5
-type_transition init tmpfs:file init_tmpfs;
-#line 5
-allow init init_tmpfs:file { read write };
-#line 5
-
-
-#line 6
-typeattribute init relabeltodomain;
-#line 6
-
-# add a rule to handle unlabelled mounts
-allow init unlabeled:filesystem mount;
-
-allow init self:capability { sys_rawio mknod };
-
-allow init dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-allow init fs_type:filesystem *;
-allow init {fs_type dev_type file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-allow init kernel:security load_policy;
-allow init usermodehelper:file { { getattr open read ioctl lock } { open append write } };
-allow init proc_security:file { { getattr open read ioctl lock } { open append write } };
-
-# Transitions to seclabel processes in init.rc
-allow init adbd:process transition;
-allow init healthd:process transition;
-allow init recovery:process transition;
-allow init shell:process transition;
-allow init ueventd:process transition;
-allow init watchdogd:process transition;
-#line 1 "external/sepolicy/inputflinger.te"
-# inputflinger
-type inputflinger, domain;
-
-#line 3
-typeattribute inputflinger mlstrustedsubject;
-#line 3
-typeattribute inputflinger unconfineddomain;
-#line 3
-
-type inputflinger_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init inputflinger_exec:file { getattr open read execute };
-#line 6
-allow init inputflinger:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow inputflinger inputflinger_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow inputflinger init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init inputflinger:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init inputflinger:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init inputflinger_exec:process inputflinger;
-#line 6
-
-#line 6
-
-#line 6
-type inputflinger_tmpfs, file_type;
-#line 6
-type_transition inputflinger tmpfs:file inputflinger_tmpfs;
-#line 6
-allow inputflinger inputflinger_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-# Call the servicemanager and transfer references to it.
-#line 7
-allow inputflinger servicemanager:binder { call transfer };
-#line 7
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 7
-# all domains in domain.te.
-#line 7
-
-
-#line 8
-typeattribute inputflinger binderservicedomain;
-#line 8
-
-#line 1 "external/sepolicy/installd.te"
-# installer daemon
-type installd, domain;
-type installd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init installd_exec:file { getattr open read execute };
-#line 5
-allow init installd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow installd installd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow installd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init installd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init installd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init installd_exec:process installd;
-#line 5
-
-#line 5
-
-#line 5
-type installd_tmpfs, file_type;
-#line 5
-type_transition installd tmpfs:file installd_tmpfs;
-#line 5
-allow installd installd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute installd relabeltodomain;
-#line 6
-
-typeattribute installd mlstrustedsubject;
-allow installd self:capability { chown dac_override fowner fsetid setgid setuid };
-allow installd system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow installd system_data_file:lnk_file create;
-allow installd dalvikcache_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow installd data_file_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow installd data_file_type:dir { relabelfrom relabelto };
-allow installd data_file_type:{ { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } { getattr unlink };
-allow installd apk_data_file:file { getattr open read ioctl lock };
-allow installd apk_tmp_file:file { getattr open read ioctl lock };
-allow installd system_file:file { getattr execute execute_no_trans };
-allow installd cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow installd download_file:dir { { open getattr read search ioctl } write remove_name };
-allow installd download_file:file { { getattr open read ioctl lock } unlink };
-dontaudit installd self:capability sys_admin;
-# Check validity of SELinux context before use.
-
-#line 23
-allow installd selinuxfs:dir { open getattr read search ioctl };
-#line 23
-allow installd selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 23
-allow installd kernel:security check_context;
-#line 23
-
-# Read /seapp_contexts and /data/security/seapp_contexts
-
-#line 25
-allow installd security_file:dir { open getattr read search ioctl };
-#line 25
-allow installd security_file:file { getattr open read ioctl lock };
-#line 25
-allow installd security_file:lnk_file { getattr open read ioctl lock };
-#line 25
-allow installd selinuxfs:dir { open getattr read search ioctl };
-#line 25
-allow installd selinuxfs:file { getattr open read ioctl lock };
-#line 25
-allow installd rootfs:dir { open getattr read search ioctl };
-#line 25
-allow installd rootfs:file { getattr open read ioctl lock };
-#line 25
-
-# ASEC
-allow installd platform_app_data_file:lnk_file { create setattr };
-allow installd app_data_file:lnk_file { create setattr };
-allow installd asec_apk_file:file { getattr open read ioctl lock };
-allow installd bluetooth_data_file:lnk_file { create setattr };
-allow installd nfc_data_file:lnk_file { create setattr };
-allow installd radio_data_file:lnk_file { create setattr };
-allow installd shell_data_file:lnk_file { create setattr };
-#line 1 "external/sepolicy/isolated_app.te"
-###
-### Services with isolatedProcess=true in their manifest.
-###
-### This file defines the rules for isolated apps. An "isolated
-### app" is an APP with UID between AID_ISOLATED_START (99000)
-### and AID_ISOLATED_END (99999).
-###
-### isolated_app includes all the appdomain rules, plus the
-### additional following rules:
-###
-
-type isolated_app, domain;
-
-#line 13
-typeattribute isolated_app appdomain;
-#line 13
-# Label ashmem objects with our own unique type.
-#line 13
-
-#line 13
-type isolated_app_tmpfs, file_type;
-#line 13
-type_transition isolated_app tmpfs:file isolated_app_tmpfs;
-#line 13
-allow isolated_app isolated_app_tmpfs:file { read write };
-#line 13
-
-#line 13
-# Map with PROT_EXEC.
-#line 13
-allow isolated_app isolated_app_tmpfs:file execute;
-#line 13
-
-
-# Already connected, unnamed sockets being passed over some other IPC
-# hence no sock_file or connectto permission. This appears to be how
-# Chrome works, may need to be updated as more apps using isolated services
-# are examined.
-allow isolated_app appdomain:unix_stream_socket { read write };
-
-allow isolated_app dalvikcache_data_file:file execute;
-allow isolated_app apk_data_file:dir getattr;
-#line 1 "external/sepolicy/kernel.te"
-# Life begins with the kernel.
-type kernel, domain;
-
-allow kernel init:process dyntransition;
-
-# The kernel is unconfined.
-
-#line 7
-typeattribute kernel mlstrustedsubject;
-#line 7
-typeattribute kernel unconfineddomain;
-#line 7
-
-
-#line 8
-typeattribute kernel relabeltodomain;
-#line 8
-
-
-allow kernel {fs_type dev_type file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-allow kernel unlabeled:filesystem mount;
-allow kernel fs_type:filesystem *;
-
-# Initial setenforce by init prior to switching to init domain.
-allow kernel self:security setenforce;
-
-# Set checkreqprot by init.rc prior to switching to init domain.
-allow kernel self:security setcheckreqprot;
-
-# For operations performed by kernel or init prior to switching to init domain.
-## TODO: Investigate whether it is safe to remove these
-allow kernel self:capability { sys_rawio mknod };
-auditallow kernel self:capability { sys_rawio mknod };
-allow kernel dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-auditallow kernel dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/keystore.te"
-type keystore, domain;
-type keystore_exec, exec_type, file_type;
-
-# keystore daemon
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init keystore_exec:file { getattr open read execute };
-#line 5
-allow init keystore:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow keystore keystore_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow keystore init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init keystore:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init keystore:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init keystore_exec:process keystore;
-#line 5
-
-#line 5
-
-#line 5
-type keystore_tmpfs, file_type;
-#line 5
-type_transition keystore tmpfs:file keystore_tmpfs;
-#line 5
-allow keystore keystore_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute keystore mlstrustedsubject;
-
-#line 7
-# Call the servicemanager and transfer references to it.
-#line 7
-allow keystore servicemanager:binder { call transfer };
-#line 7
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 7
-# all domains in domain.te.
-#line 7
-
-
-#line 8
-typeattribute keystore binderservicedomain;
-#line 8
-
-allow keystore keystore_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow keystore keystore_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow keystore keystore_exec:file { getattr };
-allow keystore tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow keystore tee:unix_stream_socket connectto;
-#line 1 "external/sepolicy/lmkd.te"
-# lmkd low memory killer daemon
-type lmkd, domain;
-type lmkd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init lmkd_exec:file { getattr open read execute };
-#line 5
-allow init lmkd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow lmkd lmkd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow lmkd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init lmkd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init lmkd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init lmkd_exec:process lmkd;
-#line 5
-
-#line 5
-
-#line 5
-type lmkd_tmpfs, file_type;
-#line 5
-type_transition lmkd tmpfs:file lmkd_tmpfs;
-#line 5
-allow lmkd lmkd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-allow lmkd self:capability { dac_override sys_resource };
-
-## Open and write to /proc/PID/oom_score_adj
-## TODO: maybe scope this down?
-
-#line 11
-allow lmkd appdomain:dir { open getattr read search ioctl };
-#line 11
-allow lmkd appdomain:{ file lnk_file } { getattr open read ioctl lock };
-#line 11
-
-allow lmkd appdomain:file write;
-
-#line 13
-allow lmkd system_server:dir { open getattr read search ioctl };
-#line 13
-allow lmkd system_server:{ file lnk_file } { getattr open read ioctl lock };
-#line 13
-
-allow lmkd system_server:file write;
-
-## Writes to /sys/module/lowmemorykiller/parameters/minfree
-allow lmkd sysfs_lowmemorykiller:file { open append write };
-#line 1 "external/sepolicy/logd.te"
-# android user-space log manager
-type logd, domain;
-type logd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init logd_exec:file { getattr open read execute };
-#line 5
-allow init logd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow logd logd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow logd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init logd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init logd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init logd_exec:process logd;
-#line 5
-
-#line 5
-
-#line 5
-type logd_tmpfs, file_type;
-#line 5
-type_transition logd tmpfs:file logd_tmpfs;
-#line 5
-allow logd logd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-allow logd self:unix_stream_socket *;
-
-allow logd self:capability { setuid setgid sys_nice };
-
-
-#line 10
-allow logd domain:dir { open getattr read search ioctl };
-#line 10
-allow logd domain:{ file lnk_file } { getattr open read ioctl lock };
-#line 10
-
-
-#line 17
-
-
-###
-### Neverallow rules
-###
-### logd should NEVER do any of this
-
-# Block device access.
-neverallow logd dev_type:blk_file { read write };
-
-# ptrace any other app
-neverallow logd domain:process ptrace;
-
-# Write to /system.
-neverallow logd system_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-
-# Write to files in /data/data or system files on /data
-neverallow logd { app_data_file system_data_file }:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-#line 1 "external/sepolicy/media_app.te"
-###
-### Apps signed with the media key.
-###
-
-type media_app, domain;
-
-#line 6
-typeattribute media_app appdomain;
-#line 6
-# Label ashmem objects with our own unique type.
-#line 6
-
-#line 6
-type media_app_tmpfs, file_type;
-#line 6
-type_transition media_app tmpfs:file media_app_tmpfs;
-#line 6
-allow media_app media_app_tmpfs:file { read write };
-#line 6
-
-#line 6
-# Map with PROT_EXEC.
-#line 6
-allow media_app media_app_tmpfs:file execute;
-#line 6
-
-
-#line 7
-typeattribute media_app platformappdomain;
-#line 7
-typeattribute media_app mlstrustedsubject;
-#line 7
-
-
-#line 8
-typeattribute media_app binderservicedomain;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute media_app netdomain;
-#line 10
-
-# Access /dev/mtp_usb.
-allow media_app mtp_device:chr_file { { getattr open read ioctl lock } { open append write } };
-# Write to /cache.
-allow media_app cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow media_app cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Stat /cache/lost+found
-allow media_app unlabeled:file getattr;
-allow media_app unlabeled:dir getattr;
-# Stat /cache/backup
-allow media_app cache_backup_file:file getattr;
-allow media_app cache_backup_file:dir getattr;
-# Read files in the rootdir (in particular, file_contexts for restorecon).
-allow media_app rootfs:file { getattr open read ioctl lock };
-allow media_app download_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow media_app download_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Allow platform apps to mark platform app data files as download files
-
-#line 27
-typeattribute media_app relabeltodomain;
-#line 27
-
-allow media_app platform_app_data_file:dir relabelfrom;
-allow media_app download_file:dir relabelto;
-#line 1 "external/sepolicy/mediaserver.te"
-# mediaserver - multimedia daemon
-type mediaserver, domain;
-
-#line 3
-typeattribute mediaserver mlstrustedsubject;
-#line 3
-typeattribute mediaserver unconfineddomain;
-#line 3
-
-type mediaserver_exec, exec_type, file_type;
-
-typeattribute mediaserver mlstrustedsubject;
-
-
-#line 8
-typeattribute mediaserver netdomain;
-#line 8
-
-
-#line 9
-
-#line 9
-# Allow the necessary permissions.
-#line 9
-
-#line 9
-# Old domain may exec the file and transition to the new domain.
-#line 9
-allow init mediaserver_exec:file { getattr open read execute };
-#line 9
-allow init mediaserver:process transition;
-#line 9
-# New domain is entered by executing the file.
-#line 9
-allow mediaserver mediaserver_exec:file { entrypoint read execute };
-#line 9
-# New domain can send SIGCHLD to its caller.
-#line 9
-allow mediaserver init:process sigchld;
-#line 9
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 9
-dontaudit init mediaserver:process noatsecure;
-#line 9
-# XXX dontaudit candidate but requires further study.
-#line 9
-allow init mediaserver:process { siginh rlimitinh };
-#line 9
-
-#line 9
-# Make the transition occur by default.
-#line 9
-type_transition init mediaserver_exec:process mediaserver;
-#line 9
-
-#line 9
-
-#line 9
-type mediaserver_tmpfs, file_type;
-#line 9
-type_transition mediaserver tmpfs:file mediaserver_tmpfs;
-#line 9
-allow mediaserver mediaserver_tmpfs:file { read write };
-#line 9
-
-#line 9
-
-
-#line 10
-allow mediaserver property_socket:sock_file write;
-#line 10
-allow mediaserver init:unix_stream_socket connectto;
-#line 10
-
-
-
-#line 12
-allow mediaserver sdcard_type:dir { open getattr read search ioctl };
-#line 12
-allow mediaserver sdcard_type:{ file lnk_file } { getattr open read ioctl lock };
-#line 12
-
-
-
-#line 14
-# Call the servicemanager and transfer references to it.
-#line 14
-allow mediaserver servicemanager:binder { call transfer };
-#line 14
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 14
-# all domains in domain.te.
-#line 14
-
-
-#line 15
-# Call the server domain and optionally transfer references to it.
-#line 15
-allow mediaserver binderservicedomain:binder { call transfer };
-#line 15
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 15
-allow binderservicedomain mediaserver:binder transfer;
-#line 15
-# Receive and use open files from the server.
-#line 15
-allow mediaserver binderservicedomain:fd use;
-#line 15
-
-
-#line 16
-# Call the server domain and optionally transfer references to it.
-#line 16
-allow mediaserver appdomain:binder { call transfer };
-#line 16
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 16
-allow appdomain mediaserver:binder transfer;
-#line 16
-# Receive and use open files from the server.
-#line 16
-allow mediaserver appdomain:fd use;
-#line 16
-
-
-#line 17
-typeattribute mediaserver binderservicedomain;
-#line 17
-
-
-allow mediaserver self:process execmem;
-allow mediaserver kernel:system module_request;
-allow mediaserver media_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow mediaserver media_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow mediaserver app_data_file:dir search;
-allow mediaserver app_data_file:file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver platform_app_data_file:file { getattr read };
-allow mediaserver sdcard_type:file write;
-allow mediaserver { gpu_device graphics_device }:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver video_device:dir { open getattr read search ioctl };
-allow mediaserver video_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver audio_device:dir { open getattr read search ioctl };
-allow mediaserver qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver audio_prop:property_service set;
-
-# Access audio devices at all.
-allow mediaserver audio_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# XXX Label with a specific type?
-allow mediaserver sysfs:file { { getattr open read ioctl lock } { open append write } };
-
-# XXX Why?
-allow mediaserver apk_data_file:file { read getattr };
-
-# Access camera device.
-allow mediaserver camera_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver rpmsg_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Inter System processes communicate over named pipe (FIFO)
-allow mediaserver system_server:fifo_file { getattr open read ioctl lock };
-
-# Camera data
-
-#line 52
-allow mediaserver camera_data_file:dir { open getattr read search ioctl };
-#line 52
-allow mediaserver camera_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 52
-
-
-#line 53
-allow mediaserver media_rw_data_file:dir { open getattr read search ioctl };
-#line 53
-allow mediaserver media_rw_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 53
-
-
-# Grant access to audio files to mediaserver
-allow mediaserver audio_data_file:dir { { open getattr read search ioctl } add_name write };
-allow mediaserver audio_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read/[write] to /proc/net/xt_qtaguid/ctrl and /dev/xt_qtaguid
-allow mediaserver qtaguid_proc:file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver qtaguid_device:chr_file { getattr open read ioctl lock };
-
-# Allow abstract socket connection
-allow mediaserver rild:unix_stream_socket { connectto read write setopt };
-
-# Needed on some devices for playing DRM protected content,
-# but seems expected and appropriate for all devices.
-
-#line 68
-allow mediaserver drmserver_socket:sock_file write;
-#line 68
-allow mediaserver drmserver:unix_stream_socket connectto;
-#line 68
-
-
-# Needed on some devices for playing audio on paired BT device,
-# but seems appropriate for all devices.
-
-#line 72
-allow mediaserver bluetooth_socket:sock_file write;
-#line 72
-allow mediaserver bluetooth:unix_stream_socket connectto;
-#line 72
-
-#line 1 "external/sepolicy/mtp.te"
-# vpn tunneling protocol manager
-type mtp, domain;
-
-#line 3
-typeattribute mtp mlstrustedsubject;
-#line 3
-typeattribute mtp unconfineddomain;
-#line 3
-
-type mtp_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init mtp_exec:file { getattr open read execute };
-#line 6
-allow init mtp:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow mtp mtp_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow mtp init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init mtp:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init mtp:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init mtp_exec:process mtp;
-#line 6
-
-#line 6
-
-#line 6
-type mtp_tmpfs, file_type;
-#line 6
-type_transition mtp tmpfs:file mtp_tmpfs;
-#line 6
-allow mtp mtp_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute mtp netdomain;
-#line 7
-
-
-# pptp policy
-allow mtp self:tcp_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow mtp self:socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow mtp self:rawip_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow mtp self:capability net_raw;
-allow mtp ppp:process signal;
-allow mtp port:tcp_socket name_connect;
-allow mtp vpn_data_file:dir search;
-#line 1 "external/sepolicy/netd.te"
-# network manager
-type netd, domain;
-type netd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init netd_exec:file { getattr open read execute };
-#line 5
-allow init netd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow netd netd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow netd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init netd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init netd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init netd_exec:process netd;
-#line 5
-
-#line 5
-
-#line 5
-type netd_tmpfs, file_type;
-#line 5
-type_transition netd tmpfs:file netd_tmpfs;
-#line 5
-allow netd netd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute netd netdomain;
-#line 6
-
-
-allow netd self:capability { net_admin net_raw kill fsetid };
-allow netd self:netlink_kobject_uevent_socket *;
-allow netd self:netlink_route_socket *;
-allow netd self:netlink_nflog_socket *;
-allow netd self:rawip_socket *;
-allow netd self:unix_stream_socket *;
-allow netd shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow netd system_file:file { getattr execute execute_no_trans };
-allow netd devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# For /proc/sys/net/ipv[46]/route/flush.
-allow netd proc_net:file write;
-
-# For /sys/modules/bcmdhd/parameters/firmware_path
-# XXX Split into its own type.
-allow netd sysfs:file write;
-
-# Set dhcp lease for PAN connection
-
-#line 26
-allow netd property_socket:sock_file write;
-#line 26
-allow netd init:unix_stream_socket connectto;
-#line 26
-
-allow netd system_prop:property_service set;
-
-# Connect to PAN
-
-#line 30
-# Allow the necessary permissions.
-#line 30
-
-#line 30
-# Old domain may exec the file and transition to the new domain.
-#line 30
-allow netd dhcp_exec:file { getattr open read execute };
-#line 30
-allow netd dhcp:process transition;
-#line 30
-# New domain is entered by executing the file.
-#line 30
-allow dhcp dhcp_exec:file { entrypoint read execute };
-#line 30
-# New domain can send SIGCHLD to its caller.
-#line 30
-allow dhcp netd:process sigchld;
-#line 30
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 30
-dontaudit netd dhcp:process noatsecure;
-#line 30
-# XXX dontaudit candidate but requires further study.
-#line 30
-allow netd dhcp:process { siginh rlimitinh };
-#line 30
-
-#line 30
-# Make the transition occur by default.
-#line 30
-type_transition netd dhcp_exec:process dhcp;
-#line 30
-
-allow netd dhcp:process signal;
-
-# Needed to update /data/misc/wifi/hostapd.conf
-# TODO: See what we can do to reduce the need for
-# these capabilities
-allow netd self:capability { dac_override chown fowner };
-allow netd wifi_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow netd wifi_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-
-# Allow netd to spawn hostapd in it's own domain
-
-#line 41
-# Allow the necessary permissions.
-#line 41
-
-#line 41
-# Old domain may exec the file and transition to the new domain.
-#line 41
-allow netd hostapd_exec:file { getattr open read execute };
-#line 41
-allow netd hostapd:process transition;
-#line 41
-# New domain is entered by executing the file.
-#line 41
-allow hostapd hostapd_exec:file { entrypoint read execute };
-#line 41
-# New domain can send SIGCHLD to its caller.
-#line 41
-allow hostapd netd:process sigchld;
-#line 41
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 41
-dontaudit netd hostapd:process noatsecure;
-#line 41
-# XXX dontaudit candidate but requires further study.
-#line 41
-allow netd hostapd:process { siginh rlimitinh };
-#line 41
-
-#line 41
-# Make the transition occur by default.
-#line 41
-type_transition netd hostapd_exec:process hostapd;
-#line 41
-
-allow netd hostapd:process signal;
-
-# Allow netd to spawn dnsmasq in it's own domain
-
-#line 45
-# Allow the necessary permissions.
-#line 45
-
-#line 45
-# Old domain may exec the file and transition to the new domain.
-#line 45
-allow netd dnsmasq_exec:file { getattr open read execute };
-#line 45
-allow netd dnsmasq:process transition;
-#line 45
-# New domain is entered by executing the file.
-#line 45
-allow dnsmasq dnsmasq_exec:file { entrypoint read execute };
-#line 45
-# New domain can send SIGCHLD to its caller.
-#line 45
-allow dnsmasq netd:process sigchld;
-#line 45
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 45
-dontaudit netd dnsmasq:process noatsecure;
-#line 45
-# XXX dontaudit candidate but requires further study.
-#line 45
-allow netd dnsmasq:process { siginh rlimitinh };
-#line 45
-
-#line 45
-# Make the transition occur by default.
-#line 45
-type_transition netd dnsmasq_exec:process dnsmasq;
-#line 45
-
-allow netd dnsmasq:process signal;
-
-# Allow netd to start clatd in its own domain
-
-#line 49
-# Allow the necessary permissions.
-#line 49
-
-#line 49
-# Old domain may exec the file and transition to the new domain.
-#line 49
-allow netd clatd_exec:file { getattr open read execute };
-#line 49
-allow netd clatd:process transition;
-#line 49
-# New domain is entered by executing the file.
-#line 49
-allow clatd clatd_exec:file { entrypoint read execute };
-#line 49
-# New domain can send SIGCHLD to its caller.
-#line 49
-allow clatd netd:process sigchld;
-#line 49
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 49
-dontaudit netd clatd:process noatsecure;
-#line 49
-# XXX dontaudit candidate but requires further study.
-#line 49
-allow netd clatd:process { siginh rlimitinh };
-#line 49
-
-#line 49
-# Make the transition occur by default.
-#line 49
-type_transition netd clatd_exec:process clatd;
-#line 49
-
-allow netd clatd:process signal;
-
-# Support netd running mdnsd
-# TODO: prune this back further
-allow netd ctl_default_prop:property_service set;
-allow netd device:sock_file write;
-
-###
-### Neverallow rules
-###
-### netd should NEVER do any of this
-
-# Block device access.
-neverallow netd dev_type:blk_file { read write };
-
-# Setting SELinux enforcing status or booleans.
-neverallow netd kernel:security { setenforce setbool };
-
-# Load security policy.
-neverallow netd kernel:security load_policy;
-
-# ptrace any other app
-neverallow netd { domain }:process ptrace;
-
-# Write to /system.
-neverallow netd system_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-
-# Write to files in /data/data or system files on /data
-neverallow netd { app_data_file system_data_file }:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-#line 1 "external/sepolicy/net.te"
-# Network types
-type node, node_type;
-type netif, netif_type;
-type port, port_type;
-
-# Use network sockets.
-allow netdomain self:{ tcp_socket udp_socket } *;
-# Connect to ports.
-allow netdomain port_type:tcp_socket name_connect;
-# Bind to ports.
-allow netdomain node_type:{ tcp_socket udp_socket } node_bind;
-allow netdomain port_type:udp_socket name_bind;
-allow netdomain port_type:tcp_socket name_bind;
-# Get route information.
-allow netdomain self:netlink_route_socket { create bind read nlmsg_read };
-
-# Talks to netd via dnsproxyd socket.
-
-#line 18
-allow netdomain dnsproxyd_socket:sock_file write;
-#line 18
-allow netdomain netd:unix_stream_socket connectto;
-#line 18
-
-#line 1 "external/sepolicy/nfc.te"
-# nfc subsystem
-type nfc, domain;
-
-#line 3
-typeattribute nfc appdomain;
-#line 3
-# Label ashmem objects with our own unique type.
-#line 3
-
-#line 3
-type nfc_tmpfs, file_type;
-#line 3
-type_transition nfc tmpfs:file nfc_tmpfs;
-#line 3
-allow nfc nfc_tmpfs:file { read write };
-#line 3
-
-#line 3
-# Map with PROT_EXEC.
-#line 3
-allow nfc nfc_tmpfs:file execute;
-#line 3
-
-
-#line 4
-typeattribute nfc binderservicedomain;
-#line 4
-
-
-# NFC device access.
-allow nfc nfc_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Data file accesses.
-allow nfc nfc_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow nfc nfc_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-allow nfc sysfs_nfc_power_writable:file { { getattr open read ioctl lock } { open append write } };
-allow nfc sysfs:file write;
-
-allow nfc sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow nfc sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/platform_app.te"
-###
-### Apps signed with the platform key.
-###
-
-type platform_app, domain;
-
-#line 6
-typeattribute platform_app mlstrustedsubject;
-#line 6
-typeattribute platform_app unconfineddomain;
-#line 6
-
-
-#line 7
-typeattribute platform_app appdomain;
-#line 7
-# Label ashmem objects with our own unique type.
-#line 7
-
-#line 7
-type platform_app_tmpfs, file_type;
-#line 7
-type_transition platform_app tmpfs:file platform_app_tmpfs;
-#line 7
-allow platform_app platform_app_tmpfs:file { read write };
-#line 7
-
-#line 7
-# Map with PROT_EXEC.
-#line 7
-allow platform_app platform_app_tmpfs:file execute;
-#line 7
-
-
-#line 8
-typeattribute platform_app platformappdomain;
-#line 8
-typeattribute platform_app mlstrustedsubject;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute platform_app netdomain;
-#line 10
-
-# Access bluetooth.
-
-#line 12
-typeattribute platform_app bluetoothdomain;
-#line 12
-
-# Write to /cache.
-allow platform_app cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow platform_app cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Read from /data/local.
-allow platform_app shell_data_file:dir search;
-allow platform_app shell_data_file:file { open getattr read };
-allow platform_app shell_data_file:lnk_file read;
-# Populate /data/app/vmdl*.tmp, /data/app-private/vmdl*.tmp files
-# created by system server.
-allow platform_app { apk_tmp_file apk_private_tmp_file }:file { { getattr open read ioctl lock } { open append write } };
-allow platform_app apk_private_data_file:dir search;
-# ASEC
-allow platform_app asec_apk_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platform_app asec_apk_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Access download files.
-allow platform_app download_file:file { { getattr open read ioctl lock } { open append write } };
-# Allow BackupManagerService to backup all app domains
-allow platform_app appdomain:fifo_file write;
-
-#
-# Rules for all platform app domains.
-#
-
-# App sandbox file accesses.
-allow platformappdomain platform_app_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platformappdomain platform_app_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow platformappdomain platform_app_data_file:file execute;
-# App sdcard file accesses
-allow platformappdomain sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platformappdomain sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Access to /data/media.
-allow platformappdomain media_rw_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platformappdomain media_rw_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/ppp.te"
-# Point to Point Protocol daemon
-type ppp, domain;
-
-#line 3
-typeattribute ppp mlstrustedsubject;
-#line 3
-typeattribute ppp unconfineddomain;
-#line 3
-
-type ppp_device, dev_type;
-type ppp_exec, exec_type, file_type;
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow mtp ppp_exec:file { getattr open read execute };
-#line 6
-allow mtp ppp:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow ppp ppp_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow ppp mtp:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit mtp ppp:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow mtp ppp:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition mtp ppp_exec:process ppp;
-#line 6
-
-
-allow ppp mtp:socket { ioctl read getattr write setattr append bind connect getopt setopt shutdown };
-allow ppp ppp_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow ppp self:capability net_admin;
-allow ppp self:udp_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow ppp system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow ppp vpn_data_file:dir { open search write add_name remove_name };
-allow ppp vpn_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow ppp mtp:fd use;
-#line 1 "external/sepolicy/property.te"
-type default_prop, property_type;
-type shell_prop, property_type;
-type debug_prop, property_type;
-type debuggerd_prop, property_type;
-type radio_prop, property_type;
-type system_prop, property_type;
-type vold_prop, property_type;
-type rild_prop, property_type;
-type ctl_default_prop, property_type;
-type ctl_dumpstate_prop, property_type;
-type ctl_rildaemon_prop, property_type;
-type audio_prop, property_type;
-type security_prop, property_type;
-type bluetooth_prop, property_type;
-type powerctl_prop, property_type;
-#line 1 "external/sepolicy/qemud.te"
-# qemu support daemon
-type qemud, domain;
-type qemud_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init qemud_exec:file { getattr open read execute };
-#line 5
-allow init qemud:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow qemud qemud_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow qemud init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init qemud:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init qemud:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init qemud_exec:process qemud;
-#line 5
-
-#line 5
-
-#line 5
-type qemud_tmpfs, file_type;
-#line 5
-type_transition qemud tmpfs:file qemud_tmpfs;
-#line 5
-allow qemud qemud_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute qemud mlstrustedsubject;
-#line 6
-typeattribute qemud unconfineddomain;
-#line 1 "external/sepolicy/racoon.te"
-# IKE key management daemon
-type racoon, domain;
-
-#line 3
-typeattribute racoon mlstrustedsubject;
-#line 3
-typeattribute racoon unconfineddomain;
-#line 3
-
-type racoon_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init racoon_exec:file { getattr open read execute };
-#line 6
-allow init racoon:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow racoon racoon_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow racoon init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init racoon:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init racoon:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init racoon_exec:process racoon;
-#line 6
-
-#line 6
-
-#line 6
-type racoon_tmpfs, file_type;
-#line 6
-type_transition racoon tmpfs:file racoon_tmpfs;
-#line 6
-allow racoon racoon_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-typeattribute racoon mlstrustedsubject;
-
-
-#line 9
-# Call the server domain and optionally transfer references to it.
-#line 9
-allow racoon servicemanager:binder { call transfer };
-#line 9
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 9
-allow servicemanager racoon:binder transfer;
-#line 9
-# Receive and use open files from the server.
-#line 9
-allow racoon servicemanager:fd use;
-#line 9
-
-
-#line 10
-# Call the server domain and optionally transfer references to it.
-#line 10
-allow racoon keystore:binder { call transfer };
-#line 10
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 10
-allow keystore racoon:binder transfer;
-#line 10
-# Receive and use open files from the server.
-#line 10
-allow racoon keystore:fd use;
-#line 10
-
-
-allow racoon tun_device:chr_file { getattr open read ioctl lock };
-allow racoon cgroup:dir { add_name create };
-allow racoon kernel:system module_request;
-allow racoon port:udp_socket name_bind;
-allow racoon node:udp_socket node_bind;
-
-allow racoon self:{ key_socket udp_socket } { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow racoon self:tun_socket create;
-allow racoon self:capability { net_admin net_bind_service net_raw setuid };
-
-# XXX: should we give ip-up-vpn its own label (currently racoon domain)
-allow racoon system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow racoon vpn_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow racoon vpn_data_file:dir { open search write add_name remove_name };
-#line 1 "external/sepolicy/radio.te"
-# phone subsystem
-type radio, domain;
-
-#line 3
-typeattribute radio appdomain;
-#line 3
-# Label ashmem objects with our own unique type.
-#line 3
-
-#line 3
-type radio_tmpfs, file_type;
-#line 3
-type_transition radio tmpfs:file radio_tmpfs;
-#line 3
-allow radio radio_tmpfs:file { read write };
-#line 3
-
-#line 3
-# Map with PROT_EXEC.
-#line 3
-allow radio radio_tmpfs:file execute;
-#line 3
-
-
-#line 4
-typeattribute radio netdomain;
-#line 4
-
-
-#line 5
-typeattribute radio bluetoothdomain;
-#line 5
-
-
-#line 6
-typeattribute radio binderservicedomain;
-#line 6
-
-
-# Talks to init via the property socket.
-
-#line 9
-allow radio property_socket:sock_file write;
-#line 9
-allow radio init:unix_stream_socket connectto;
-#line 9
-
-
-# Talks to rild via the rild socket.
-
-#line 12
-allow radio rild_socket:sock_file write;
-#line 12
-allow radio rild:unix_stream_socket connectto;
-#line 12
-
-
-# Data file accesses.
-allow radio radio_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow radio radio_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-allow radio alarm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Property service
-allow radio radio_prop:property_service set;
-
-# ctl interface
-allow radio ctl_rildaemon_prop:property_service set;
-#line 1 "external/sepolicy/recovery.te"
-# recovery console (used in recovery init.rc for /sbin/recovery)
-type recovery, domain;
-allow recovery rootfs:file entrypoint;
-
-#line 4
-typeattribute recovery mlstrustedsubject;
-#line 4
-typeattribute recovery unconfineddomain;
-#line 4
-
-
-#line 5
-typeattribute recovery relabeltodomain;
-#line 5
-
-
-allow recovery self:capability2 mac_admin;
-
-allow recovery {fs_type dev_type -kmem_device file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-allow recovery unlabeled:filesystem mount;
-allow recovery fs_type:filesystem *;
-
-# Required to e.g. wipe userdata/cache.
-allow recovery dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-
-allow recovery self:process execmem;
-allow recovery ashmem_device:chr_file execute;
-allow recovery tmpfs:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-## TODO: Investigate whether it is safe to remove these
-allow recovery self:capability { sys_rawio mknod };
-auditallow recovery self:capability { sys_rawio mknod };
-#line 1 "external/sepolicy/release_app.te"
-###
-### Apps signed with the release key (testkey in AOSP).
-###
-
-type release_app, domain;
-
-#line 6
-typeattribute release_app mlstrustedsubject;
-#line 6
-typeattribute release_app unconfineddomain;
-#line 6
-
-
-#line 7
-typeattribute release_app appdomain;
-#line 7
-# Label ashmem objects with our own unique type.
-#line 7
-
-#line 7
-type release_app_tmpfs, file_type;
-#line 7
-type_transition release_app tmpfs:file release_app_tmpfs;
-#line 7
-allow release_app release_app_tmpfs:file { read write };
-#line 7
-
-#line 7
-# Map with PROT_EXEC.
-#line 7
-allow release_app release_app_tmpfs:file execute;
-#line 7
-
-
-#line 8
-typeattribute release_app platformappdomain;
-#line 8
-typeattribute release_app mlstrustedsubject;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute release_app netdomain;
-#line 10
-
-# Access bluetooth.
-
-#line 12
-typeattribute release_app bluetoothdomain;
-#line 12
-
-
-# Write to /cache.
-allow release_app cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow release_app cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/rild.te"
-# rild - radio interface layer daemon
-type rild, domain;
-
-#line 3
-typeattribute rild mlstrustedsubject;
-#line 3
-typeattribute rild unconfineddomain;
-#line 3
-
-type rild_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init rild_exec:file { getattr open read execute };
-#line 6
-allow init rild:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow rild rild_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow rild init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init rild:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init rild:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init rild_exec:process rild;
-#line 6
-
-#line 6
-
-#line 6
-type rild_tmpfs, file_type;
-#line 6
-type_transition rild tmpfs:file rild_tmpfs;
-#line 6
-allow rild rild_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute rild netdomain;
-#line 7
-
-allow rild self:netlink_route_socket { setopt write };
-allow rild kernel:system module_request;
-
-#line 10
-allow rild property_socket:sock_file write;
-#line 10
-allow rild init:unix_stream_socket connectto;
-#line 10
-
-
-#line 11
-allow rild qemud_socket:sock_file write;
-#line 11
-allow rild qemud:unix_stream_socket connectto;
-#line 11
-
-allow rild self:capability { setuid net_admin net_raw };
-allow rild alarm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow rild cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow rild radio_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow rild radio_device:blk_file { getattr open read ioctl lock };
-allow rild qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow rild mtd_device:dir search;
-allow rild efs_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow rild efs_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow rild shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow rild bluetooth_efs_file:file { getattr open read ioctl lock };
-allow rild bluetooth_efs_file:dir { open getattr read search ioctl };
-allow rild radio_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow rild radio_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow rild sdcard_type:dir { open getattr read search ioctl };
-allow rild system_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow rild system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow rild system_file:file { getattr execute execute_no_trans };
-dontaudit rild self:capability sys_admin;
-
-# property service
-allow rild rild_prop:property_service set;
-allow rild radio_prop:property_service set;
-
-# Read/Write to uart driver (for GPS)
-allow rild gps_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-allow rild tty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Allow rild to create, bind, read, write to itself through a netlink socket
-allow rild self:netlink_socket { create bind read write };
-
-allow rild self:netlink_kobject_uevent_socket { bind create getopt read setopt };
-
-# Access to wake locks
-allow rild sysfs_wake_lock:file { { getattr open read ioctl lock } { open append write } };
-
-allow rild self:socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-#line 1 "external/sepolicy/runas.te"
-type runas, domain, mlstrustedsubject;
-type runas_exec, exec_type, file_type;
-
-# ndk-gdb invokes adb shell run-as.
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow shell runas_exec:file { getattr open read execute };
-#line 5
-allow shell runas:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow runas runas_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow runas shell:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit shell runas:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow shell runas:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition shell runas_exec:process runas;
-#line 5
-
-allow runas adbd:process sigchld;
-allow runas shell:fd  use;
-allow runas devpts:chr_file { read write ioctl };
-
-# run-as reads package information.
-allow runas system_data_file:file { getattr open read ioctl lock };
-
-# run-as checks and changes to the app data dir.
-dontaudit runas self:capability dac_override;
-allow runas app_data_file:dir { getattr search };
-
-# run-as switches to the app UID/GID.
-allow runas self:capability { setuid setgid };
-
-# run-as switches to the app security context.
-# read /seapp_contexts and /data/security/seapp_contexts
-
-#line 22
-allow runas security_file:dir { open getattr read search ioctl };
-#line 22
-allow runas security_file:file { getattr open read ioctl lock };
-#line 22
-allow runas security_file:lnk_file { getattr open read ioctl lock };
-#line 22
-allow runas selinuxfs:dir { open getattr read search ioctl };
-#line 22
-allow runas selinuxfs:file { getattr open read ioctl lock };
-#line 22
-allow runas rootfs:dir { open getattr read search ioctl };
-#line 22
-allow runas rootfs:file { getattr open read ioctl lock };
-#line 22
-
-
-#line 23
-allow runas selinuxfs:dir { open getattr read search ioctl };
-#line 23
-allow runas selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 23
-allow runas kernel:security check_context;
-#line 23
- # validate context
-allow runas { appdomain -system_app }:process dyntransition; # setcon
-#line 1 "external/sepolicy/sdcardd.te"
-type sdcardd, domain;
-type sdcardd_exec, exec_type, file_type;
-
-
-#line 4
-
-#line 4
-# Allow the necessary permissions.
-#line 4
-
-#line 4
-# Old domain may exec the file and transition to the new domain.
-#line 4
-allow init sdcardd_exec:file { getattr open read execute };
-#line 4
-allow init sdcardd:process transition;
-#line 4
-# New domain is entered by executing the file.
-#line 4
-allow sdcardd sdcardd_exec:file { entrypoint read execute };
-#line 4
-# New domain can send SIGCHLD to its caller.
-#line 4
-allow sdcardd init:process sigchld;
-#line 4
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 4
-dontaudit init sdcardd:process noatsecure;
-#line 4
-# XXX dontaudit candidate but requires further study.
-#line 4
-allow init sdcardd:process { siginh rlimitinh };
-#line 4
-
-#line 4
-# Make the transition occur by default.
-#line 4
-type_transition init sdcardd_exec:process sdcardd;
-#line 4
-
-#line 4
-
-#line 4
-type sdcardd_tmpfs, file_type;
-#line 4
-type_transition sdcardd tmpfs:file sdcardd_tmpfs;
-#line 4
-allow sdcardd sdcardd_tmpfs:file { read write };
-#line 4
-
-#line 4
-
-
-allow sdcardd cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd fuse_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow sdcardd rootfs:dir mounton;
-allow sdcardd sdcard_type:filesystem mount;
-allow sdcardd self:capability { setuid setgid dac_override sys_admin sys_resource };
-
-allow sdcardd sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-type_transition sdcardd system_data_file:{ dir file } media_rw_data_file;
-allow sdcardd media_rw_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd media_rw_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read /data/system/packages.list.
-allow sdcardd system_data_file:file { getattr open read ioctl lock };
-
-# Compatibility for existing devices with /data/media in system_data_file.
-# TODO: Remove these lines after we have guaranteed that /data/media has been relabeled to media_rw_data_file.
-allow sdcardd system_data_file:dir  { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/servicemanager.te"
-# servicemanager - the Binder context manager
-type servicemanager, domain;
-type servicemanager_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init servicemanager_exec:file { getattr open read execute };
-#line 5
-allow init servicemanager:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow servicemanager servicemanager_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow servicemanager init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init servicemanager:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init servicemanager:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init servicemanager_exec:process servicemanager;
-#line 5
-
-#line 5
-
-#line 5
-type servicemanager_tmpfs, file_type;
-#line 5
-type_transition servicemanager tmpfs:file servicemanager_tmpfs;
-#line 5
-allow servicemanager servicemanager_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-# Note that we do not use the binder_* macros here.
-# servicemanager is unique in that it only provides
-# name service (aka context manager) for Binder.
-# As such, it only ever receives and transfers other references
-# created by other domains.  It never passes its own references
-# or initiates a Binder IPC.
-allow servicemanager self:binder set_context_mgr;
-allow servicemanager domain:binder transfer;
-#line 1 "external/sepolicy/shared_app.te"
-###
-### Apps signed with the shared key.
-###
-
-type shared_app, domain;
-
-#line 6
-typeattribute shared_app mlstrustedsubject;
-#line 6
-typeattribute shared_app unconfineddomain;
-#line 6
-
-
-#line 7
-typeattribute shared_app appdomain;
-#line 7
-# Label ashmem objects with our own unique type.
-#line 7
-
-#line 7
-type shared_app_tmpfs, file_type;
-#line 7
-type_transition shared_app tmpfs:file shared_app_tmpfs;
-#line 7
-allow shared_app shared_app_tmpfs:file { read write };
-#line 7
-
-#line 7
-# Map with PROT_EXEC.
-#line 7
-allow shared_app shared_app_tmpfs:file execute;
-#line 7
-
-
-#line 8
-typeattribute shared_app platformappdomain;
-#line 8
-typeattribute shared_app mlstrustedsubject;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute shared_app netdomain;
-#line 10
-
-# Access bluetooth.
-
-#line 12
-typeattribute shared_app bluetoothdomain;
-#line 12
-
-#line 1 "external/sepolicy/shelldomain.te"
-# Rules for all shell domains (e.g. console service and adb shell).
-
-# Access /data/local/tmp.
-allow shelldomain shell_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow shelldomain shell_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow shelldomain shell_data_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# Access sdcard.
-allow shelldomain sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow shelldomain sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# adb bugreport
-
-#line 13
-allow shelldomain dumpstate_socket:sock_file write;
-#line 13
-allow shelldomain dumpstate:unix_stream_socket connectto;
-#line 13
-
-
-allow shelldomain rootfs:dir { open getattr read search ioctl };
-allow shelldomain devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain tty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain console_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain input_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain system_file:file { getattr execute execute_no_trans };
-allow shelldomain shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow shelldomain zygote_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-
-#line 24
-allow shelldomain apk_data_file:dir { open getattr read search ioctl };
-#line 24
-allow shelldomain apk_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 24
-
-
-# Set properties.
-
-#line 27
-allow shelldomain property_socket:sock_file write;
-#line 27
-allow shelldomain init:unix_stream_socket connectto;
-#line 27
-
-allow shelldomain shell_prop:property_service set;
-allow shelldomain ctl_dumpstate_prop:property_service set;
-allow shelldomain debug_prop:property_service set;
-allow shelldomain powerctl_prop:property_service set;
-
-# ndk-gdb invokes adb shell ps to find the app PID.
-
-#line 34
-allow shelldomain { appdomain -system_app }:dir { open getattr read search ioctl };
-#line 34
-allow shelldomain { appdomain -system_app }:{ file lnk_file } { getattr open read ioctl lock };
-#line 34
-
-
-# ndk-gdb invokes adb shell ls to check the app data dir.
-allow shelldomain app_data_file:dir search;
-
-# ps and ps -Z output for app processes.
-
-#line 40
-allow shelldomain appdomain:dir { open getattr read search ioctl };
-#line 40
-allow shelldomain appdomain:{ file lnk_file } { getattr open read ioctl lock };
-#line 40
-
-allow shelldomain appdomain:process getattr;
-#line 1 "external/sepolicy/shell.te"
-# Domain for shell processes spawned by ADB
-type shell, domain, shelldomain, mlstrustedsubject;
-type shell_exec, exec_type, file_type;
-
-# Create and use network sockets.
-
-#line 6
-typeattribute shell netdomain;
-#line 6
-
-
-# Run app_process.
-# XXX Transition into its own domain?
-
-#line 10
-typeattribute shell appdomain;
-#line 10
-# Label ashmem objects with our own unique type.
-#line 10
-
-#line 10
-type shell_tmpfs, file_type;
-#line 10
-type_transition shell tmpfs:file shell_tmpfs;
-#line 10
-allow shell shell_tmpfs:file { read write };
-#line 10
-
-#line 10
-# Map with PROT_EXEC.
-#line 10
-allow shell shell_tmpfs:file execute;
-#line 10
-
-
-# inherits from shelldomain.te
-#line 1 "external/sepolicy/surfaceflinger.te"
-# surfaceflinger - display compositor service
-type surfaceflinger, domain;
-
-#line 3
-typeattribute surfaceflinger mlstrustedsubject;
-#line 3
-typeattribute surfaceflinger unconfineddomain;
-#line 3
-
-type surfaceflinger_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init surfaceflinger_exec:file { getattr open read execute };
-#line 6
-allow init surfaceflinger:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow surfaceflinger surfaceflinger_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow surfaceflinger init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init surfaceflinger:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init surfaceflinger:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init surfaceflinger_exec:process surfaceflinger;
-#line 6
-
-#line 6
-
-#line 6
-type surfaceflinger_tmpfs, file_type;
-#line 6
-type_transition surfaceflinger tmpfs:file surfaceflinger_tmpfs;
-#line 6
-allow surfaceflinger surfaceflinger_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-typeattribute surfaceflinger mlstrustedsubject;
-
-# Talk to init over the property socket.
-
-#line 10
-allow surfaceflinger property_socket:sock_file write;
-#line 10
-allow surfaceflinger init:unix_stream_socket connectto;
-#line 10
-
-
-# Perform Binder IPC.
-
-#line 13
-# Call the servicemanager and transfer references to it.
-#line 13
-allow surfaceflinger servicemanager:binder { call transfer };
-#line 13
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 13
-# all domains in domain.te.
-#line 13
-
-
-#line 14
-# Call the server domain and optionally transfer references to it.
-#line 14
-allow surfaceflinger system_server:binder { call transfer };
-#line 14
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 14
-allow system_server surfaceflinger:binder transfer;
-#line 14
-# Receive and use open files from the server.
-#line 14
-allow surfaceflinger system_server:fd use;
-#line 14
-
-
-#line 15
-# Call the server domain and optionally transfer references to it.
-#line 15
-allow surfaceflinger nfc:binder { call transfer };
-#line 15
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 15
-allow nfc surfaceflinger:binder transfer;
-#line 15
-# Receive and use open files from the server.
-#line 15
-allow surfaceflinger nfc:fd use;
-#line 15
-
-
-#line 16
-# Call the server domain and optionally transfer references to it.
-#line 16
-allow surfaceflinger mediaserver:binder { call transfer };
-#line 16
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 16
-allow mediaserver surfaceflinger:binder transfer;
-#line 16
-# Receive and use open files from the server.
-#line 16
-allow surfaceflinger mediaserver:fd use;
-#line 16
-
-
-#line 17
-typeattribute surfaceflinger binderservicedomain;
-#line 17
-
-
-# Access the GPU.
-allow surfaceflinger gpu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Access /dev/graphics/fb0.
-allow surfaceflinger graphics_device:dir search;
-allow surfaceflinger graphics_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Access /dev/video1.
-allow surfaceflinger video_device:dir { open getattr read search ioctl };
-allow surfaceflinger video_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Create and use netlink kobject uevent sockets.
-allow surfaceflinger self:netlink_kobject_uevent_socket *;
-
-# Set properties.
-allow surfaceflinger system_prop:property_service set;
-allow surfaceflinger ctl_default_prop:property_service set;
-
-# Use open files supplied by an app.
-allow surfaceflinger appdomain:fd use;
-allow surfaceflinger platform_app_data_file:file { read write };
-allow surfaceflinger app_data_file:file { read write };
-
-# Use open file provided by bootanim.
-allow surfaceflinger bootanim:fd use;
-
-# Allow a dumpstate triggered screenshot
-
-#line 46
-# Call the server domain and optionally transfer references to it.
-#line 46
-allow surfaceflinger dumpstate:binder { call transfer };
-#line 46
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 46
-allow dumpstate surfaceflinger:binder transfer;
-#line 46
-# Receive and use open files from the server.
-#line 46
-allow surfaceflinger dumpstate:fd use;
-#line 46
-
-
-#line 47
-# Call the server domain and optionally transfer references to it.
-#line 47
-allow surfaceflinger shell:binder { call transfer };
-#line 47
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 47
-allow shell surfaceflinger:binder transfer;
-#line 47
-# Receive and use open files from the server.
-#line 47
-allow surfaceflinger shell:fd use;
-#line 47
-
-
-# Needed on some devices for playing DRM protected content,
-# but seems expected and appropriate for all devices.
-allow surfaceflinger tee:unix_stream_socket connectto;
-allow surfaceflinger tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/su.te"
-# File types must be defined for file_contexts.
-type su_exec, exec_type, file_type;
-
-#line 23
-
-#line 1 "external/sepolicy/system_app.te"
-#
-# Apps that run with the system UID, e.g. com.android.system.ui,
-# com.android.settings.  These are not as privileged as the system
-# server.
-#
-type system_app, domain;
-
-#line 7
-typeattribute system_app mlstrustedsubject;
-#line 7
-typeattribute system_app unconfineddomain;
-#line 7
-
-
-#line 8
-typeattribute system_app appdomain;
-#line 8
-# Label ashmem objects with our own unique type.
-#line 8
-
-#line 8
-type system_app_tmpfs, file_type;
-#line 8
-type_transition system_app tmpfs:file system_app_tmpfs;
-#line 8
-allow system_app system_app_tmpfs:file { read write };
-#line 8
-
-#line 8
-# Map with PROT_EXEC.
-#line 8
-allow system_app system_app_tmpfs:file execute;
-#line 8
-
-
-#line 9
-typeattribute system_app binderservicedomain;
-#line 9
-
-
-# Perform binder IPC to any app domain.
-
-#line 12
-# Call the server domain and optionally transfer references to it.
-#line 12
-allow system_app appdomain:binder { call transfer };
-#line 12
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 12
-allow appdomain system_app:binder transfer;
-#line 12
-# Receive and use open files from the server.
-#line 12
-allow system_app appdomain:fd use;
-#line 12
-
-
-# Read and write system data files.
-# May want to split into separate types.
-allow system_app system_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow system_app system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read wallpaper file.
-allow system_app wallpaper_file:file { getattr open read ioctl lock };
-
-# Write to dalvikcache.
-allow system_app dalvikcache_data_file:file { write setattr };
-
-# Talk to keystore.
-
-#line 26
-allow system_app keystore_socket:sock_file write;
-#line 26
-allow system_app keystore:unix_stream_socket connectto;
-#line 26
-
-
-# Read SELinux enforcing status.
-
-#line 29
-allow system_app selinuxfs:dir { open getattr read search ioctl };
-#line 29
-allow system_app selinuxfs:file { getattr open read ioctl lock };
-#line 29
-
-
-# Settings app reads sdcard for storage stats
-allow system_app sdcard_type:dir { open getattr read search ioctl };
-
-# Write to properties
-
-#line 35
-allow system_app property_socket:sock_file write;
-#line 35
-allow system_app init:unix_stream_socket connectto;
-#line 35
-
-allow system_app debug_prop:property_service set;
-allow system_app radio_prop:property_service set;
-allow system_app system_prop:property_service set;
-#line 1 "external/sepolicy/system_server.te"
-#
-# System Server aka system_server spawned by zygote.
-# Most of the framework services run in this process.
-#
-type system_server, domain, mlstrustedsubject;
-
-#line 6
-typeattribute system_server mlstrustedsubject;
-#line 6
-typeattribute system_server unconfineddomain;
-#line 6
-
-
-# Define a type for tmpfs-backed ashmem regions.
-
-#line 9
-type system_server_tmpfs, file_type;
-#line 9
-type_transition system_server tmpfs:file system_server_tmpfs;
-#line 9
-allow system_server system_server_tmpfs:file { read write };
-#line 9
-
-
-# Dalvik Compiler JIT Mapping.
-allow system_server self:process execmem;
-allow system_server ashmem_device:chr_file execute;
-allow system_server system_server_tmpfs:file execute;
-
-# For art.
-allow system_server dalvikcache_data_file:file execute;
-
-# Child of the zygote.
-allow system_server zygote:fd use;
-allow system_server zygote:process sigchld;
-allow system_server zygote_tmpfs:file read;
-
-# Needed to close the zygote socket, which involves getopt / getattr
-# This should be deleted after b/12061011 is fixed
-allow system_server zygote:unix_stream_socket { getopt getattr };
-
-# system server gets network and bluetooth permissions.
-
-#line 29
-typeattribute system_server netdomain;
-#line 29
-
-
-#line 30
-typeattribute system_server bluetoothdomain;
-#line 30
-
-
-# These are the capabilities assigned by the zygote to the
-# system server.
-allow system_server self:capability {
-    kill
-    net_admin
-    net_bind_service
-    net_broadcast
-    net_raw
-    sys_boot
-    sys_module
-    sys_nice
-    sys_resource
-    sys_time
-    sys_tty_config
-};
-
-allow system_server self:capability2 block_suspend;
-
-# Triggered by /proc/pid accesses, not allowed.
-dontaudit system_server self:capability sys_ptrace;
-
-# Trigger module auto-load.
-allow system_server kernel:system module_request;
-
-# Use netlink uevent sockets.
-allow system_server self:netlink_kobject_uevent_socket *;
-
-# Kill apps.
-allow system_server appdomain:process { sigkill signal };
-
-# Set scheduling info for apps.
-allow system_server appdomain:process { getsched setsched };
-allow system_server mediaserver:process { getsched setsched };
-
-# Read /proc data for apps.
-allow system_server appdomain:dir { open getattr read search ioctl };
-allow system_server appdomain:{ file lnk_file } { { getattr open read ioctl lock } { open append write } };
-
-# Read/Write to /proc/net/xt_qtaguid/ctrl and and /dev/xt_qtaguid.
-allow system_server qtaguid_proc:file { { getattr open read ioctl lock } { open append write } };
-allow system_server qtaguid_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Read /sys/kernel/debug/wakeup_sources.
-allow system_server debugfs:file { getattr open read ioctl lock };
-
-# WifiWatchdog uses a packet_socket
-allow system_server self:packet_socket *;
-
-# 3rd party VPN clients require a tun_socket to be created
-allow system_server self:tun_socket create;
-
-# Notify init of death.
-allow system_server init:process sigchld;
-
-# Talk to init and various daemons via sockets.
-
-#line 87
-allow system_server property_socket:sock_file write;
-#line 87
-allow system_server init:unix_stream_socket connectto;
-#line 87
-
-
-#line 88
-allow system_server qemud_socket:sock_file write;
-#line 88
-allow system_server qemud:unix_stream_socket connectto;
-#line 88
-
-
-#line 89
-allow system_server installd_socket:sock_file write;
-#line 89
-allow system_server installd:unix_stream_socket connectto;
-#line 89
-
-
-#line 90
-allow system_server lmkd_socket:sock_file write;
-#line 90
-allow system_server lmkd:unix_stream_socket connectto;
-#line 90
-
-
-#line 91
-allow system_server netd_socket:sock_file write;
-#line 91
-allow system_server netd:unix_stream_socket connectto;
-#line 91
-
-
-#line 92
-allow system_server vold_socket:sock_file write;
-#line 92
-allow system_server vold:unix_stream_socket connectto;
-#line 92
-
-
-#line 93
-allow system_server zygote_socket:sock_file write;
-#line 93
-allow system_server zygote:unix_stream_socket connectto;
-#line 93
-
-
-#line 94
-allow system_server keystore_socket:sock_file write;
-#line 94
-allow system_server keystore:unix_stream_socket connectto;
-#line 94
-
-
-#line 95
-allow system_server gps_socket:sock_file write;
-#line 95
-allow system_server gpsd:unix_stream_socket connectto;
-#line 95
-
-
-#line 96
-allow system_server racoon_socket:sock_file write;
-#line 96
-allow system_server racoon:unix_stream_socket connectto;
-#line 96
-
-
-#line 97
-allow system_server wpa_socket:sock_file write;
-#line 97
-allow system_server wpa:unix_dgram_socket sendto;
-#line 97
-
-
-# Communicate over a socket created by surfaceflinger.
-allow system_server surfaceflinger:unix_stream_socket { read write setopt };
-
-# Perform Binder IPC.
-
-#line 103
-# Call the servicemanager and transfer references to it.
-#line 103
-allow system_server servicemanager:binder { call transfer };
-#line 103
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 103
-# all domains in domain.te.
-#line 103
-
-
-#line 104
-# Call the server domain and optionally transfer references to it.
-#line 104
-allow system_server binderservicedomain:binder { call transfer };
-#line 104
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 104
-allow binderservicedomain system_server:binder transfer;
-#line 104
-# Receive and use open files from the server.
-#line 104
-allow system_server binderservicedomain:fd use;
-#line 104
-
-
-#line 105
-# Call the server domain and optionally transfer references to it.
-#line 105
-allow system_server appdomain:binder { call transfer };
-#line 105
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 105
-allow appdomain system_server:binder transfer;
-#line 105
-# Receive and use open files from the server.
-#line 105
-allow system_server appdomain:fd use;
-#line 105
-
-
-#line 106
-# Call the server domain and optionally transfer references to it.
-#line 106
-allow system_server healthd:binder { call transfer };
-#line 106
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 106
-allow healthd system_server:binder transfer;
-#line 106
-# Receive and use open files from the server.
-#line 106
-allow system_server healthd:fd use;
-#line 106
-
-
-#line 107
-# Call the server domain and optionally transfer references to it.
-#line 107
-allow system_server dumpstate:binder { call transfer };
-#line 107
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 107
-allow dumpstate system_server:binder transfer;
-#line 107
-# Receive and use open files from the server.
-#line 107
-allow system_server dumpstate:fd use;
-#line 107
-
-
-#line 108
-typeattribute system_server binderservicedomain;
-#line 108
-
-
-# Read /proc/pid files for Binder clients.
-
-#line 111
-allow system_server appdomain:dir { open getattr read search ioctl };
-#line 111
-allow system_server appdomain:{ file lnk_file } { getattr open read ioctl lock };
-#line 111
-
-
-#line 112
-allow system_server mediaserver:dir { open getattr read search ioctl };
-#line 112
-allow system_server mediaserver:{ file lnk_file } { getattr open read ioctl lock };
-#line 112
-
-allow system_server appdomain:process getattr;
-allow system_server mediaserver:process getattr;
-
-# Check SELinux permissions.
-
-#line 117
-allow system_server selinuxfs:dir { open getattr read search ioctl };
-#line 117
-allow system_server selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 117
-allow system_server kernel:security compute_av;
-#line 117
-allow system_server self:netlink_selinux_socket *;
-#line 117
-
-
-# XXX Label sysfs files with a specific type?
-allow system_server sysfs:file { { getattr open read ioctl lock } { open append write } };
-allow system_server sysfs_nfc_power_writable:file { { getattr open read ioctl lock } { open append write } };
-
-# Access devices.
-allow system_server device:dir { open getattr read search ioctl };
-allow system_server mdns_socket:sock_file { { getattr open read ioctl lock } { open append write } };
-allow system_server alarm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server gpu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server graphics_device:dir search;
-allow system_server graphics_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server iio_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server input_device:dir { open getattr read search ioctl };
-allow system_server input_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server tty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server urandom_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server usbaccessory_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server video_device:dir { open getattr read search ioctl };
-allow system_server video_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server adbd_socket:sock_file { { getattr open read ioctl lock } { open append write } };
-
-# tun device used for 3rd party vpn apps
-allow system_server tun_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Manage data files.
-allow system_server data_file_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow system_server data_file_type:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read /file_contexts and /data/security/file_contexts
-
-#line 149
-allow system_server security_file:dir { open getattr read search ioctl };
-#line 149
-allow system_server security_file:file { getattr open read ioctl lock };
-#line 149
-allow system_server security_file:lnk_file { getattr open read ioctl lock };
-#line 149
-allow system_server selinuxfs:dir { open getattr read search ioctl };
-#line 149
-allow system_server selinuxfs:file { getattr open read ioctl lock };
-#line 149
-allow system_server rootfs:dir { open getattr read search ioctl };
-#line 149
-allow system_server rootfs:file { getattr open read ioctl lock };
-#line 149
-
-
-# Relabel apk files.
-
-#line 152
-typeattribute system_server relabeltodomain;
-#line 152
-
-allow system_server { apk_tmp_file apk_private_tmp_file }:file { relabelfrom relabelto };
-allow system_server { apk_data_file apk_private_data_file }:file { relabelfrom relabelto };
-
-# Relabel wallpaper.
-allow system_server system_data_file:file relabelfrom;
-allow system_server wallpaper_file:file relabelto;
-allow system_server wallpaper_file:file { { getattr open read ioctl lock } { open append write } };
-
-# Relabel /data/anr.
-allow system_server system_data_file:dir relabelfrom;
-allow system_server anr_data_file:dir relabelto;
-
-# Property Service write
-allow system_server system_prop:property_service set;
-allow system_server radio_prop:property_service set;
-allow system_server debug_prop:property_service set;
-allow system_server powerctl_prop:property_service set;
-
-# ctl interface
-allow system_server ctl_default_prop:property_service set;
-
-# Create a socket for receiving info from wpa.
-type_transition system_server wifi_data_file:sock_file system_wpa_socket;
-type_transition system_server wpa_socket:sock_file system_wpa_socket;
-allow system_server wpa_socket:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow system_server system_wpa_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Remove sockets created by wpa_supplicant
-allow system_server wpa_socket:sock_file unlink;
-
-# Create a socket for connections from debuggerd.
-type_transition system_server system_data_file:sock_file system_ndebug_socket "ndebugsocket";
-allow system_server system_ndebug_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Specify any arguments to zygote.
-allow system_server self:zygote { specifyids specifyrlimits specifyseinfo };
-
-# Manage cache files.
-allow system_server cache_file:dir { relabelfrom { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } };
-allow system_server cache_file:file { relabelfrom { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } } };
-
-# Run system programs, e.g. dexopt.
-allow system_server system_file:file { getattr execute execute_no_trans };
-
-# Allow reading of /proc/pid data for other domains.
-# XXX dontaudit candidate
-allow system_server domain:dir { open getattr read search ioctl };
-allow system_server domain:file { getattr open read ioctl lock };
-
-# LocationManager(e.g, GPS) needs to read and write
-# to uart driver and ctrl proc entry
-allow system_server gps_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server gps_control:file { { getattr open read ioctl lock } { open append write } };
-
-# Allow system_server to use app-created sockets.
-allow system_server appdomain:{ tcp_socket udp_socket } { setopt read write };
-
-# Allow abstract socket connection
-allow system_server rild:unix_stream_socket connectto;
-
-# connect to vpn tunnel
-allow system_server mtp:unix_stream_socket { connectto };
-
-# BackupManagerService lets PMS create a data backup file
-allow system_server cache_backup_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Relabel /data/backup
-allow system_server backup_data_file:dir { relabelto relabelfrom };
-# Relabel /cache/.*\.{data|restore}
-allow system_server cache_backup_file:file { relabelto relabelfrom };
-# LocalTransport creates and relabels /cache/backup
-allow system_server cache_backup_file:dir { relabelto relabelfrom { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } };
-
-# Allow system to talk to usb device
-allow system_server usb_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server usb_device:dir { open getattr read search ioctl };
-
-# Allow system to talk to sensors
-allow system_server sensors_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Read from HW RNG (needed by EntropyMixer).
-allow system_server hw_random_device:chr_file { getattr open read ioctl lock };
-
-# Access to wake locks
-allow system_server sysfs_wake_lock:file { { getattr open read ioctl lock } { open append write } };
-
-# Read and delete files under /dev/fscklogs.
-
-#line 239
-allow system_server fscklogs:dir { open getattr read search ioctl };
-#line 239
-allow system_server fscklogs:{ file lnk_file } { getattr open read ioctl lock };
-#line 239
-
-allow system_server fscklogs:dir { write remove_name };
-allow system_server fscklogs:file unlink;
-
-# For SELinuxPolicyInstallReceiver
-
-#line 244
-
-#line 244
-allow system_server security_file:dir { open getattr read search ioctl };
-#line 244
-allow system_server security_file:file { getattr open read ioctl lock };
-#line 244
-allow system_server security_file:lnk_file { getattr open read ioctl lock };
-#line 244
-allow system_server selinuxfs:dir { open getattr read search ioctl };
-#line 244
-allow system_server selinuxfs:file { getattr open read ioctl lock };
-#line 244
-allow system_server rootfs:dir { open getattr read search ioctl };
-#line 244
-allow system_server rootfs:file { getattr open read ioctl lock };
-#line 244
-
-#line 244
-
-#line 244
-allow system_server property_socket:sock_file write;
-#line 244
-allow system_server init:unix_stream_socket connectto;
-#line 244
-
-#line 244
-allow system_server security_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-#line 244
-allow system_server security_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 244
-allow system_server security_file:lnk_file { create rename unlink };
-#line 244
-allow system_server security_prop:property_service set;
-#line 244
-
-
-# For legacy unlabeled userdata on existing devices.
-# See discussion of Unlabeled files in domain.te for more information.
-# This rule is for dalvikcache mmap/mprotect PROT_EXEC.
-allow system_server unlabeled:file execute;
-
-# logd access, system_server inherit logd write socket
-# (urge is to deprecate this long term)
-allow system_server zygote:unix_dgram_socket write;
-
-# Be consistent with DAC permissions. Allow system_server to write to
-# /sys/module/lowmemorykiller/parameters/adj
-# /sys/module/lowmemorykiller/parameters/minfree
-allow system_server sysfs_lowmemorykiller:file { open append write };
-#line 1 "external/sepolicy/tee.te"
-##
-# trusted execution environment (tee) daemon
-#
-type tee, domain;
-type tee_exec, exec_type, file_type;
-type tee_device, dev_type;
-type tee_data_file, file_type, data_file_type;
-
-
-#line 9
-
-#line 9
-# Allow the necessary permissions.
-#line 9
-
-#line 9
-# Old domain may exec the file and transition to the new domain.
-#line 9
-allow init tee_exec:file { getattr open read execute };
-#line 9
-allow init tee:process transition;
-#line 9
-# New domain is entered by executing the file.
-#line 9
-allow tee tee_exec:file { entrypoint read execute };
-#line 9
-# New domain can send SIGCHLD to its caller.
-#line 9
-allow tee init:process sigchld;
-#line 9
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 9
-dontaudit init tee:process noatsecure;
-#line 9
-# XXX dontaudit candidate but requires further study.
-#line 9
-allow init tee:process { siginh rlimitinh };
-#line 9
-
-#line 9
-# Make the transition occur by default.
-#line 9
-type_transition init tee_exec:process tee;
-#line 9
-
-#line 9
-
-#line 9
-type tee_tmpfs, file_type;
-#line 9
-type_transition tee tmpfs:file tee_tmpfs;
-#line 9
-allow tee tee_tmpfs:file { read write };
-#line 9
-
-#line 9
-
-allow tee self:capability { dac_override };
-allow tee tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow tee tee_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow tee tee_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow tee self:netlink_socket { create bind read };
-#line 1 "external/sepolicy/ueventd.te"
-# ueventd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type ueventd, domain;
-
-#line 4
-type ueventd_tmpfs, file_type;
-#line 4
-type_transition ueventd tmpfs:file ueventd_tmpfs;
-#line 4
-allow ueventd ueventd_tmpfs:file { read write };
-#line 4
-
-
-#line 5
-type_transition ueventd device:chr_file klog_device "__kmsg__";
-#line 5
-allow ueventd klog_device:chr_file { create open write unlink };
-#line 5
-allow ueventd device:dir { write add_name remove_name };
-#line 5
-
-
-#line 6
-allow ueventd security_file:dir { open getattr read search ioctl };
-#line 6
-allow ueventd security_file:file { getattr open read ioctl lock };
-#line 6
-allow ueventd security_file:lnk_file { getattr open read ioctl lock };
-#line 6
-allow ueventd selinuxfs:dir { open getattr read search ioctl };
-#line 6
-allow ueventd selinuxfs:file { getattr open read ioctl lock };
-#line 6
-allow ueventd rootfs:dir { open getattr read search ioctl };
-#line 6
-allow ueventd rootfs:file { getattr open read ioctl lock };
-#line 6
-
-
-#line 7
-typeattribute ueventd relabeltodomain;
-#line 7
-
-allow ueventd rootfs:file entrypoint;
-allow ueventd init:process sigchld;
-allow ueventd self:capability { chown mknod net_admin setgid fsetid sys_rawio dac_override fowner };
-allow ueventd device:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow ueventd device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow ueventd sysfs:file { { getattr open read ioctl lock } { open append write } };
-allow ueventd sysfs:file setattr;
-allow ueventd sysfs_type:file { relabelfrom relabelto };
-allow ueventd sysfs_devices_system_cpu:file { { getattr open read ioctl lock } { open append write } };
-allow ueventd tmpfs:chr_file { { getattr open read ioctl lock } { open append write } };
-allow ueventd dev_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow ueventd dev_type:lnk_file { create unlink };
-allow ueventd dev_type:chr_file { create setattr unlink };
-allow ueventd dev_type:blk_file { create setattr unlink };
-allow ueventd self:netlink_kobject_uevent_socket *;
-allow ueventd efs_file:dir search;
-allow ueventd efs_file:file { getattr open read ioctl lock };
-#line 1 "external/sepolicy/unconfined.te"
-#######################################################
-#
-# This is the unconfined template. This template is the base policy
-# which is used by daemons and other privileged components of
-# Android.
-#
-# Historically, this template was called "unconfined" because it
-# allowed the domain to do anything it wanted. Over time,
-# this has changed, and will continue to change in the future.
-# The rules in this file will be removed when no remaining
-# unconfined domains require it, or when the rules contradict
-# Android security best practices. Domains which need rules not
-# provided by the unconfined template should add them directly to
-# the relevant policy.
-#
-# The use of this template is discouraged.
-######################################################
-
-allow unconfineddomain self:capability ~{ sys_ptrace sys_rawio mknod sys_module };
-allow unconfineddomain self:capability2 ~{ mac_override mac_admin };
-allow unconfineddomain kernel:security ~{ load_policy setenforce setcheckreqprot };
-allow unconfineddomain kernel:system *;
-allow unconfineddomain domain:process ~{ execmem execstack execheap ptrace transition dyntransition };
-allow unconfineddomain domain:fd *;
-allow unconfineddomain domain:dir { open getattr read search ioctl };
-allow unconfineddomain domain:lnk_file { getattr open read ioctl lock };
-allow unconfineddomain domain:{ fifo_file file } { { getattr open read ioctl lock } { open append write } };
-allow unconfineddomain domain:{ socket tcp_socket udp_socket rawip_socket netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket appletalk_socket netlink_route_socket netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket netlink_ip6fw_socket netlink_dnrt_socket netlink_kobject_uevent_socket tun_socket } *;
-allow unconfineddomain domain:{ sem msgq shm ipc } *;
-allow unconfineddomain domain:key *;
-allow unconfineddomain {fs_type dev_type file_type}:{ dir lnk_file sock_file fifo_file } ~relabelto;
-allow unconfineddomain {fs_type -usermodehelper -proc_security}:{ chr_file file } ~{entrypoint execmod execute relabelto};
-allow unconfineddomain {dev_type -kmem_device}:{ chr_file file } ~{entrypoint execmod execute relabelto};
-allow unconfineddomain file_type:{ chr_file file } ~{entrypoint execmod execute relabelto};
-allow unconfineddomain { rootfs system_file exec_type }:file execute;
-allow unconfineddomain node_type:node *;
-allow unconfineddomain node_type:{ tcp_socket udp_socket rawip_socket } node_bind;
-allow unconfineddomain netif_type:netif *;
-allow unconfineddomain port_type:{ socket tcp_socket udp_socket rawip_socket netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket appletalk_socket netlink_route_socket netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket netlink_ip6fw_socket netlink_dnrt_socket netlink_kobject_uevent_socket tun_socket } name_bind;
-allow unconfineddomain port_type:{ tcp_socket dccp_socket } name_connect;
-allow unconfineddomain domain:peer recv;
-allow unconfineddomain { domain -init }:binder { call transfer set_context_mgr };
-allow unconfineddomain property_type:property_service set;
-#line 1 "external/sepolicy/uncrypt.te"
-# uncrypt
-type uncrypt, domain;
-type uncrypt_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init uncrypt_exec:file { getattr open read execute };
-#line 5
-allow init uncrypt:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow uncrypt uncrypt_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow uncrypt init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init uncrypt:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init uncrypt:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init uncrypt_exec:process uncrypt;
-#line 5
-
-#line 5
-
-#line 5
-type uncrypt_tmpfs, file_type;
-#line 5
-type_transition uncrypt tmpfs:file uncrypt_tmpfs;
-#line 5
-allow uncrypt uncrypt_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute uncrypt mlstrustedsubject;
-#line 6
-typeattribute uncrypt unconfineddomain;
-#line 6
-
-
-allow uncrypt self:capability dac_override;
-
-# Read OTA zip file from /data/data/com.google.android.gsf/app_download
-
-#line 11
-allow uncrypt app_data_file:dir { open getattr read search ioctl };
-#line 11
-allow uncrypt app_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 11
-
-
-#line 16
-
-
-# Create tmp file /cache/recovery/command.tmp
-# Read /cache/recovery/command
-# Rename /cache/recovery/command.tmp to /cache/recovery/command
-allow uncrypt cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow uncrypt cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Set a property to reboot the device.
-
-#line 25
-allow uncrypt property_socket:sock_file write;
-#line 25
-allow uncrypt init:unix_stream_socket connectto;
-#line 25
-
-allow uncrypt powerctl_prop:property_service set;
-
-# Raw writes to block device
-allow uncrypt self:capability sys_rawio;
-allow uncrypt block_device:blk_file { open append write };
-#line 1 "external/sepolicy/untrusted_app.te"
-###
-### Untrusted apps.
-###
-### This file defines the rules for untrusted apps. An "untrusted
-### app" is an APP with UID between APP_AID (10000)
-### and AID_ISOLATED_START (99000).
-###
-### untrusted_app includes all the appdomain rules, plus the
-### additional following rules:
-###
-
-type untrusted_app, domain;
-
-#line 13
-typeattribute untrusted_app mlstrustedsubject;
-#line 13
-typeattribute untrusted_app unconfineddomain;
-#line 13
-
-
-#line 14
-typeattribute untrusted_app appdomain;
-#line 14
-# Label ashmem objects with our own unique type.
-#line 14
-
-#line 14
-type untrusted_app_tmpfs, file_type;
-#line 14
-type_transition untrusted_app tmpfs:file untrusted_app_tmpfs;
-#line 14
-allow untrusted_app untrusted_app_tmpfs:file { read write };
-#line 14
-
-#line 14
-# Map with PROT_EXEC.
-#line 14
-allow untrusted_app untrusted_app_tmpfs:file execute;
-#line 14
-
-
-#line 15
-typeattribute untrusted_app netdomain;
-#line 15
-
-
-#line 16
-typeattribute untrusted_app bluetoothdomain;
-#line 16
-
-
-# Some apps ship with shared libraries and binaries that they write out
-# to their sandbox directory and then execute.
-allow untrusted_app app_data_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-allow untrusted_app tun_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Internal SDCard rw access.
-allow untrusted_app sdcard_internal:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow untrusted_app sdcard_internal:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# External SDCard rw access.
-allow untrusted_app sdcard_external:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow untrusted_app sdcard_external:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# ASEC
-allow untrusted_app asec_apk_file:dir { getattr };
-allow untrusted_app asec_apk_file:file { getattr open read ioctl lock };
-# Execute libs in asec containers.
-allow untrusted_app asec_public_file:file execute;
-
-# Create tcp/udp sockets
-allow untrusted_app node_type:{ tcp_socket udp_socket } node_bind;
-allow untrusted_app self:{ tcp_socket udp_socket } { { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } } accept listen };
-# Bind to a particular hostname/address/interface (e.g., localhost) instead of
-# ANY. Normally, apps should not be listening on all interfaces.
-allow untrusted_app port:{ tcp_socket udp_socket } name_bind;
-
-# Allow the allocation and use of ptys
-# Used by: https://play.google.com/store/apps/details?id=jackpal.androidterm
-
-#line 47
-# Each domain gets a unique devpts type.
-#line 47
-type untrusted_app_devpts, fs_type;
-#line 47
-# Label the pty with the unique type when created.
-#line 47
-type_transition untrusted_app devpts:chr_file untrusted_app_devpts;
-#line 47
-# Allow use of the pty after creation.
-#line 47
-allow untrusted_app untrusted_app_devpts:chr_file { open getattr read write ioctl };
-#line 47
-# Note: devpts:dir search and ptmx_device:chr_file rw_file_perms
-#line 47
-# allowed to everyone via domain.te.
-#line 47
-
-
-# Used by Finsky / Android "Verify Apps" functionality when
-# running "adb install foo.apk".
-# TODO: Long term, we don't want apps probing into shell data files.
-# Figure out a way to remove these rules.
-allow untrusted_app shell_data_file:file { getattr open read ioctl lock };
-allow untrusted_app shell_data_file:dir { open getattr read search ioctl };
-#line 1 "external/sepolicy/vold.te"
-# volume manager
-type vold, domain;
-type vold_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init vold_exec:file { getattr open read execute };
-#line 5
-allow init vold:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow vold vold_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow vold init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init vold:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init vold:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init vold_exec:process vold;
-#line 5
-
-#line 5
-
-#line 5
-type vold_tmpfs, file_type;
-#line 5
-type_transition vold tmpfs:file vold_tmpfs;
-#line 5
-allow vold vold_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-typeattribute vold mlstrustedsubject;
-allow vold system_file:file { getattr execute execute_no_trans };
-allow vold block_device:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow vold block_device:blk_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow vold device:dir write;
-allow vold devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-allow vold rootfs:dir mounton;
-allow vold sdcard_type:dir mounton;
-allow vold sdcard_type:filesystem { mount remount unmount };
-allow vold sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow vold sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow vold tmpfs:filesystem { mount unmount };
-allow vold tmpfs:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow vold tmpfs:dir mounton;
-allow vold self:capability { net_admin dac_override mknod sys_admin chown fowner fsetid };
-allow vold self:netlink_kobject_uevent_socket *;
-allow vold app_data_file:dir search;
-allow vold app_data_file:file { { getattr open read ioctl lock } { open append write } };
-allow vold loop_device:blk_file { { getattr open read ioctl lock } { open append write } };
-allow vold dm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-# For vold Process::killProcessesWithOpenFiles function.
-allow vold domain:dir { open getattr read search ioctl };
-allow vold domain:{ file lnk_file } { getattr open read ioctl lock };
-allow vold domain:process { signal sigkill };
-allow vold self:capability { sys_ptrace kill };
-
-# For blkid
-allow vold shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# XXX Label sysfs files with a specific type?
-allow vold sysfs:file { { getattr open read ioctl lock } { open append write } };
-
-
-#line 39
-type_transition vold device:chr_file klog_device "__kmsg__";
-#line 39
-allow vold klog_device:chr_file { create open write unlink };
-#line 39
-allow vold device:dir { write add_name remove_name };
-#line 39
-
-
-# Log fsck results
-allow vold fscklogs:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow vold fscklogs:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-#
-# Rules to support encrypted fs support.
-#
-
-# Set property.
-
-#line 50
-allow vold property_socket:sock_file write;
-#line 50
-allow vold init:unix_stream_socket connectto;
-#line 50
-
-
-# Unmount and mount the fs.
-allow vold labeledfs:filesystem { mount unmount remount };
-
-# Access /efs/userdata_footer.
-# XXX Split into a separate type?
-allow vold efs_file:file { { getattr open read ioctl lock } { open append write } };
-
-# Create and mount on /data/tmp_mnt.
-allow vold system_data_file:dir { create { { open getattr read search ioctl } { open search write add_name remove_name } } mounton };
-
-# Set scheduling policy of kernel processes
-allow vold kernel:process setsched;
-
-# Property Service
-allow vold vold_prop:property_service set;
-allow vold powerctl_prop:property_service set;
-allow vold ctl_default_prop:property_service set;
-
-# ASEC
-allow vold asec_image_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow vold asec_image_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-
-#line 73
-allow vold security_file:dir { open getattr read search ioctl };
-#line 73
-allow vold security_file:file { getattr open read ioctl lock };
-#line 73
-allow vold security_file:lnk_file { getattr open read ioctl lock };
-#line 73
-allow vold selinuxfs:dir { open getattr read search ioctl };
-#line 73
-allow vold selinuxfs:file { getattr open read ioctl lock };
-#line 73
-allow vold rootfs:dir { open getattr read search ioctl };
-#line 73
-allow vold rootfs:file { getattr open read ioctl lock };
-#line 73
-
-
-#line 74
-typeattribute vold relabeltodomain;
-#line 74
-
-allow vold asec_apk_file:dir { { { open getattr read search ioctl } { open search write add_name remove_name } } setattr relabelfrom };
-allow vold asec_public_file:dir { relabelto setattr };
-allow vold asec_apk_file:file { { getattr open read ioctl lock } setattr relabelfrom };
-allow vold asec_public_file:file { relabelto setattr };
-
-# Handle wake locks (used for device encryption)
-allow vold sysfs_wake_lock:file { { getattr open read ioctl lock } { open append write } };
-allow vold self:capability2 block_suspend;
-#line 1 "external/sepolicy/watchdogd.te"
-# watchdogd seclabel is specified in init.<board>.rc
-type watchdogd, domain;
-allow watchdogd rootfs:file { entrypoint { getattr open read ioctl lock } };
-allow watchdogd self:capability mknod;
-allow watchdogd device:dir { add_name write remove_name };
-allow watchdogd watchdog_device:chr_file { { getattr open read ioctl lock } { open append write } };
-# because of /dev/__kmsg__ and /dev/__null__
-
-#line 8
-type_transition watchdogd device:chr_file klog_device "__kmsg__";
-#line 8
-allow watchdogd klog_device:chr_file { create open write unlink };
-#line 8
-allow watchdogd device:dir { write add_name remove_name };
-#line 8
-
-type_transition watchdogd device:chr_file null_device "__null__";
-allow watchdogd null_device:chr_file { create unlink };
-#line 1 "external/sepolicy/wpa_supplicant.te"
-# wpa - wpa supplicant or equivalent
-type wpa, domain;
-type wpa_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init wpa_exec:file { getattr open read execute };
-#line 5
-allow init wpa:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow wpa wpa_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow wpa init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init wpa:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init wpa:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init wpa_exec:process wpa;
-#line 5
-
-#line 5
-
-#line 5
-type wpa_tmpfs, file_type;
-#line 5
-type_transition wpa tmpfs:file wpa_tmpfs;
-#line 5
-allow wpa wpa_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-allow wpa kernel:system module_request;
-allow wpa self:capability { setuid net_admin setgid net_raw };
-allow wpa cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow wpa self:netlink_route_socket *;
-allow wpa self:netlink_socket *;
-allow wpa self:packet_socket *;
-allow wpa self:udp_socket *;
-allow wpa wifi_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow wpa wifi_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-#line 15
-allow wpa system_wpa_socket:sock_file write;
-#line 15
-allow wpa system_server:unix_dgram_socket sendto;
-#line 15
-
-allow wpa random_device:chr_file { getattr open read ioctl lock };
-
-# Create a socket for receiving info from wpa
-type_transition wpa wifi_data_file:sock_file wpa_socket;
-allow wpa wpa_socket:dir { { { open getattr read search ioctl } { open search write add_name remove_name } } setattr };
-allow wpa wpa_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Allow wpa_cli to work. wpa_cli creates a socket in
-# /data/misc/wifi/sockets which wpa supplicant communicates with.
-#line 27
-
-#line 1 "external/sepolicy/zygote.te"
-# zygote
-type zygote, domain;
-type zygote_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init zygote_exec:file { getattr open read execute };
-#line 5
-allow init zygote:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow zygote zygote_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow zygote init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init zygote:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init zygote:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init zygote_exec:process zygote;
-#line 5
-
-#line 5
-
-#line 5
-type zygote_tmpfs, file_type;
-#line 5
-type_transition zygote tmpfs:file zygote_tmpfs;
-#line 5
-allow zygote zygote_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute zygote mlstrustedsubject;
-# Override DAC on files and switch uid/gid.
-allow zygote self:capability { dac_override setgid setuid fowner };
-# Drop capabilities from bounding set.
-allow zygote self:capability setpcap;
-# Switch SELinux context to app domains.
-allow zygote system_server:process dyntransition;
-allow zygote appdomain:process dyntransition;
-# Allow zygote to read app /proc/pid dirs (b/10455872)
-allow zygote appdomain:dir { getattr search };
-allow zygote appdomain:file { { getattr open read ioctl lock } };
-# Move children into the peer process group.
-allow zygote system_server:process { getpgid setpgid };
-allow zygote appdomain:process { getpgid setpgid };
-# Write to system data.
-allow zygote system_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow zygote system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow zygote dalvikcache_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow zygote dalvikcache_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# For art.
-allow zygote dalvikcache_data_file:file execute;
-# Execute dexopt.
-allow zygote system_file:file { getattr execute execute_no_trans };
-# Control cgroups.
-allow zygote cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow zygote self:capability sys_admin;
-# Check validity of SELinux context before use.
-
-#line 33
-allow zygote selinuxfs:dir { open getattr read search ioctl };
-#line 33
-allow zygote selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 33
-allow zygote kernel:security check_context;
-#line 33
-
-# Check SELinux permissions.
-
-#line 35
-allow zygote selinuxfs:dir { open getattr read search ioctl };
-#line 35
-allow zygote selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 35
-allow zygote kernel:security compute_av;
-#line 35
-allow zygote self:netlink_selinux_socket *;
-#line 35
-
-# Read /seapp_contexts and /data/security/seapp_contexts
-
-#line 37
-allow zygote security_file:dir { open getattr read search ioctl };
-#line 37
-allow zygote security_file:file { getattr open read ioctl lock };
-#line 37
-allow zygote security_file:lnk_file { getattr open read ioctl lock };
-#line 37
-allow zygote selinuxfs:dir { open getattr read search ioctl };
-#line 37
-allow zygote selinuxfs:file { getattr open read ioctl lock };
-#line 37
-allow zygote rootfs:dir { open getattr read search ioctl };
-#line 37
-allow zygote rootfs:file { getattr open read ioctl lock };
-#line 37
-
-
-# Setting up /storage/emulated.
-allow zygote rootfs:dir mounton;
-allow zygote sdcard_type:dir { write search setattr create add_name mounton };
-dontaudit zygote self:capability fsetid;
-allow zygote tmpfs:dir { write create add_name setattr mounton search };
-allow zygote tmpfs:filesystem mount;
-allow zygote labeledfs:filesystem remount;
-
-# Handle --invoke-with command when launching Zygote with a wrapper command.
-allow zygote zygote_exec:file { execute_no_trans open };
-
-# handle bugreports b/10498304
-allow zygote ashmem_device:chr_file execute;
-allow zygote shell_data_file:file { write getattr };
-allow zygote system_server:binder { transfer call };
-allow zygote servicemanager:binder { call };
-
-# For legacy unlabeled userdata on existing devices.
-# See discussion of Unlabeled files in domain.te for more information.
-# This rule is for dalvikcache mmap/mprotect PROT_EXEC.
-allow zygote unlabeled:file execute;
-#line 1 "build/target/board/generic/sepolicy/bootanim.te"
-allow bootanim self:process execmem;
-allow bootanim ashmem_device:chr_file execute;
-#line 1 "build/target/board/generic/sepolicy/domain.te"
-# For /sys/qemu_trace files in the emulator.
-allow domain sysfs_writable:file { { getattr open read ioctl lock } { open append write } };
-#line 1 "build/target/board/generic/sepolicy/surfaceflinger.te"
-allow surfaceflinger self:process execmem;
-allow surfaceflinger ashmem_device:chr_file execute;
-#line 1 "external/sepolicy/roles"
-role r;
-role r types domain;
-#line 1 "external/sepolicy/users"
-user u roles { r } level s0 range s0 - s0:c0.c1023;
-#line 1 "external/sepolicy/initial_sid_contexts"
-sid kernel u:r:kernel:s0
-sid security u:object_r:kernel:s0
-sid unlabeled u:object_r:unlabeled:s0
-sid fs u:object_r:labeledfs:s0
-sid file u:object_r:unlabeled:s0
-sid file_labels u:object_r:unlabeled:s0
-sid init u:object_r:unlabeled:s0
-sid any_socket u:object_r:unlabeled:s0
-sid port u:object_r:port:s0
-sid netif u:object_r:netif:s0
-sid netmsg u:object_r:unlabeled:s0
-sid node u:object_r:node:s0
-sid igmp_packet u:object_r:unlabeled:s0
-sid icmp_socket u:object_r:unlabeled:s0
-sid tcp_socket u:object_r:unlabeled:s0
-sid sysctl_modprobe u:object_r:unlabeled:s0
-sid sysctl u:object_r:proc:s0
-sid sysctl_fs u:object_r:unlabeled:s0
-sid sysctl_kernel u:object_r:unlabeled:s0
-sid sysctl_net u:object_r:unlabeled:s0
-sid sysctl_net_unix u:object_r:unlabeled:s0
-sid sysctl_vm u:object_r:unlabeled:s0
-sid sysctl_dev u:object_r:unlabeled:s0
-sid kmod u:object_r:unlabeled:s0
-sid policy u:object_r:unlabeled:s0
-sid scmp_packet u:object_r:unlabeled:s0
-sid devnull u:object_r:null_device:s0
-#line 1 "external/sepolicy/fs_use"
-# Label inodes via getxattr.
-fs_use_xattr yaffs2 u:object_r:labeledfs:s0;
-fs_use_xattr jffs2 u:object_r:labeledfs:s0;
-fs_use_xattr ext2 u:object_r:labeledfs:s0;
-fs_use_xattr ext3 u:object_r:labeledfs:s0;
-fs_use_xattr ext4 u:object_r:labeledfs:s0;
-fs_use_xattr xfs u:object_r:labeledfs:s0;
-fs_use_xattr btrfs u:object_r:labeledfs:s0;
-
-# Label inodes from task label.
-fs_use_task pipefs u:object_r:pipefs:s0;
-fs_use_task sockfs u:object_r:sockfs:s0;
-
-# Label inodes from combination of task label and fs label.
-# Define type_transition rules if you want per-domain types.
-fs_use_trans devpts u:object_r:devpts:s0;
-fs_use_trans tmpfs u:object_r:tmpfs:s0;
-fs_use_trans devtmpfs u:object_r:device:s0;
-fs_use_trans shm u:object_r:shm:s0;
-fs_use_trans mqueue u:object_r:mqueue:s0;
-
-#line 1 "external/sepolicy/genfs_contexts"
-# Label inodes with the fs label.
-genfscon rootfs / u:object_r:rootfs:s0
-# proc labeling can be further refined (longest matching prefix).
-genfscon proc / u:object_r:proc:s0
-genfscon proc /net u:object_r:proc_net:s0
-genfscon proc /net/xt_qtaguid/ctrl u:object_r:qtaguid_proc:s0
-genfscon proc /sys/fs/protected_hardlinks u:object_r:proc_security:s0
-genfscon proc /sys/fs/protected_symlinks u:object_r:proc_security:s0
-genfscon proc /sys/fs/suid_dumpable u:object_r:proc_security:s0
-genfscon proc /sys/kernel/core_pattern u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/dmesg_restrict u:object_r:proc_security:s0
-genfscon proc /sys/kernel/hotplug u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/kptr_restrict u:object_r:proc_security:s0
-genfscon proc /sys/kernel/modprobe u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/modules_disabled u:object_r:proc_security:s0
-genfscon proc /sys/kernel/poweroff_cmd u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/randomize_va_space u:object_r:proc_security:s0
-genfscon proc /sys/kernel/usermodehelper u:object_r:usermodehelper:s0
-genfscon proc /sys/net u:object_r:proc_net:s0
-genfscon proc /sys/vm/mmap_min_addr u:object_r:proc_security:s0
-# selinuxfs booleans can be individually labeled.
-genfscon selinuxfs / u:object_r:selinuxfs:s0
-genfscon cgroup / u:object_r:cgroup:s0
-# sysfs labels can be set by userspace.
-genfscon sysfs / u:object_r:sysfs:s0
-genfscon inotifyfs / u:object_r:inotify:s0
-genfscon vfat / u:object_r:sdcard_external:s0
-genfscon debugfs / u:object_r:debugfs:s0
-genfscon fuse / u:object_r:sdcard_internal:s0
-#line 1 "external/sepolicy/port_contexts"
-# portcon statements go here, e.g.
-# portcon tcp 80 u:object_r:http_port:s0
-
diff --git a/tools/selinux/src/gen_SELinux_CTS.py b/tools/selinux/src/gen_SELinux_CTS.py
deleted file mode 100755
index 85d49a8..0000000
--- a/tools/selinux/src/gen_SELinux_CTS.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/python
-# genCheckAccessCTS.py - takes an input SELinux policy.conf file and generates
-# an XML file based on the allow and neverallow rules.  The file contains rules,
-# which are created by expanding the SELinux rule notation into the individual
-# components which a checkAccess() check, that a policy manager would have to
-# perform, needs.
-#
-# This test does not work with all valid SELinux policy.conf files.  It is meant
-# to simply use a given AOSP generated policy.conf file to create sets
-# representing the policy's types, attributes, classes and permissions, which
-# are used to expand the allow and neverallow rules found.  For a full parser
-# and compiler of SELinux, see external/checkpolicy.
-# @dcashman
-
-import pdb
-import re
-import sys
-from xml.etree.ElementTree import Element, SubElement, tostring
-from xml.dom import minidom
-
-import SELinux_CTS
-from SELinux_CTS import SELinuxPolicy
-
-usage = "Usage: ./gen_SELinux_CTS.py input_policy_file output_xml_avc_rules_file neverallow_only=[t/f]"
-
-if __name__ == "__main__":
-    # check usage
-    if len(sys.argv) != 4:
-        print usage
-        exit()
-    input_file = sys.argv[1]
-    output_file = sys.argv[2]
-    neverallow_only = (sys.argv[3] == "neverallow_only=t")
-    policy = SELinuxPolicy()
-    policy.from_file_name(input_file) #load data from file
-
-    # expand rules into 4-tuples for SELinux.h checkAccess() check
-    xml_root = Element('SELinux_AVC_Rules')
-    if not neverallow_only:
-        count = 1
-        for a in policy.allow_rules:
-            expanded_xml = SELinux_CTS.expand_avc_rule_to_xml(policy, a, str(count), 'allow')
-            if len(expanded_xml):
-                xml_root.append(expanded_xml)
-                count += 1
-    count = 1
-    for n in policy.neverallow_rules:
-        expanded_xml = SELinux_CTS.expand_avc_rule_to_xml(policy, n, str(count), 'neverallow')
-        if len(expanded_xml):
-            xml_root.append(expanded_xml)
-            count += 1
-
-    #print out the xml file
-    s = tostring(xml_root)
-    s_parsed = minidom.parseString(s)
-    output = s_parsed.toprettyxml(indent="    ")
-    with open(output_file, 'w') as out_file:
-        out_file.write(output)
diff --git a/tools/selinux/test/policy_clean_test.conf b/tools/selinux/test/policy_clean_test.conf
deleted file mode 100644
index 074a63b..0000000
--- a/tools/selinux/test/policy_clean_test.conf
+++ /dev/null
@@ -1,2230 +0,0 @@
-#line 1 "external/sepolicy/security_classes"
-# FLASK
-
-#
-# Define the security object classes
-#
-
-# Classes marked as userspace are classes
-# for userspace object managers
-
-class capability
-
-# file-related classes
-class file
-
-#
-# Define a common prefix for file access vectors.
-#
-
-common file
-{
-	ioctl
-	read
-	write
-	create
-	getattr
-	setattr
-	lock
-	relabelfrom
-	relabelto
-	append
-	unlink
-	link
-	rename
-	execute
-	swapon
-	quotaon
-	mounton
-}
-
-class file
-inherits file
-{
-	execute_no_trans
-	entrypoint
-	execmod
-	open
-	audit_access
-}
-
-class capability
-{
-	# The capabilities are defined in include/linux/capability.h
-	# Capabilities >= 32 are defined in the capability2 class.
-	# Care should be taken to ensure that these are consistent with
-	# those definitions. (Order matters)
-
-	chown
-	dac_override
-	dac_read_search
-	fowner
-	fsetid
-	kill
-	setgid
-	setuid
-	setpcap
-	linux_immutable
-	net_bind_service
-	net_broadcast
-	net_admin
-	net_raw
-	ipc_lock
-	ipc_owner
-	sys_module
-	sys_rawio
-	sys_chroot
-	sys_ptrace
-	sys_pacct
-	sys_admin
-	sys_boot
-	sys_nice
-	sys_resource
-	sys_time
-	sys_tty_config
-	mknod
-	lease
-	audit_write
-	audit_control
-	setfcap
-}
-
-########################################
-#
-# Basic level names for system low and high
-#
-
-
-#line 1 "external/sepolicy/mls"
-#########################################
-# MLS declarations
-#
-
-# Generate the desired number of sensitivities and categories.
-
-#line 6
-# Each sensitivity has a name and zero or more aliases.
-#line 6
-sensitivity s0;
-#line 6
-
-#line 6
-
-#line 6
-# Define the ordering of the sensitivity levels (least to greatest)
-#line 6
-dominance { s0  }
-#line 6
-
-category c0;
-#line 7
-category c1;
-#line 7
-category c2;
-#line 7
-category c3;
-#line 7
-category c4;
-#line 7
-category c5;
-#line 7
-category c6;
-#line 7
-category c7;
-#line 7
-category c8;
-#line 7
-category c9;
-#line 7
-category c10;
-#line 7
-category c11;
-#line 7
-category c12;
-#line 7
-category c13;
-#line 7
-category c14;
-#line 7
-category c15;
-#line 7
-category c16;
-#line 7
-category c17;
-#line 7
-category c18;
-#line 7
-category c19;
-#line 7
-category c20;
-#line 7
-category c21;
-#line 7
-category c22;
-#line 7
-category c23;
-#line 7
-category c24;
-#line 7
-category c25;
-#line 7
-category c26;
-#line 7
-category c27;
-#line 7
-category c28;
-#line 7
-category c29;
-#line 7
-category c30;
-#line 7
-category c31;
-#line 7
-category c32;
-#line 7
-category c33;
-#line 7
-category c34;
-#line 7
-category c35;
-#line 7
-category c36;
-#line 7
-category c37;
-#line 7
-category c38;
-#line 7
-category c39;
-#line 7
-category c40;
-#line 7
-category c41;
-#line 7
-category c42;
-#line 7
-category c43;
-#line 7
-category c44;
-#line 7
-category c45;
-#line 7
-category c46;
-#line 7
-category c47;
-#line 7
-category c48;
-#line 7
-category c49;
-#line 7
-category c50;
-#line 7
-category c51;
-#line 7
-category c52;
-#line 7
-category c53;
-#line 7
-category c54;
-#line 7
-category c55;
-#line 7
-category c56;
-#line 7
-category c57;
-#line 7
-category c58;
-#line 7
-category c59;
-#line 7
-category c60;
-#line 7
-category c61;
-#line 7
-category c62;
-#line 7
-category c63;
-#line 7
-category c64;
-#line 7
-category c65;
-#line 7
-category c66;
-#line 7
-category c67;
-#line 7
-category c68;
-#line 7
-category c69;
-#line 7
-category c70;
-#line 7
-category c71;
-#line 7
-category c72;
-#line 7
-category c73;
-#line 7
-category c74;
-#line 7
-category c75;
-#line 7
-category c76;
-#line 7
-category c77;
-#line 7
-category c78;
-#line 7
-category c79;
-#line 7
-category c80;
-#line 7
-category c81;
-#line 7
-category c82;
-#line 7
-category c83;
-#line 7
-category c84;
-#line 7
-category c85;
-#line 7
-category c86;
-#line 7
-category c87;
-#line 7
-category c88;
-#line 7
-category c89;
-#line 7
-category c90;
-#line 7
-category c91;
-#line 7
-category c92;
-#line 7
-category c93;
-#line 7
-category c94;
-#line 7
-category c95;
-#line 7
-category c96;
-#line 7
-category c97;
-#line 7
-category c98;
-#line 7
-category c99;
-#line 7
-category c100;
-#line 7
-category c101;
-#line 7
-category c102;
-#line 7
-category c103;
-#line 7
-category c104;
-#line 7
-category c105;
-#line 7
-category c106;
-#line 7
-category c107;
-#line 7
-category c108;
-#line 7
-category c109;
-#line 7
-category c110;
-#line 7
-category c111;
-#line 7
-category c112;
-#line 7
-category c113;
-#line 7
-category c114;
-#line 7
-category c115;
-#line 7
-category c116;
-#line 7
-category c117;
-#line 7
-category c118;
-#line 7
-category c119;
-#line 7
-category c120;
-#line 7
-category c121;
-#line 7
-category c122;
-#line 7
-category c123;
-#line 7
-category c124;
-#line 7
-category c125;
-#line 7
-category c126;
-#line 7
-category c127;
-#line 7
-category c128;
-#line 7
-category c129;
-#line 7
-category c130;
-#line 7
-category c131;
-#line 7
-category c132;
-#line 7
-category c133;
-#line 7
-category c134;
-#line 7
-category c135;
-#line 7
-category c136;
-#line 7
-category c137;
-#line 7
-category c138;
-#line 7
-category c139;
-#line 7
-category c140;
-#line 7
-category c141;
-#line 7
-category c142;
-#line 7
-category c143;
-#line 7
-category c144;
-#line 7
-category c145;
-#line 7
-category c146;
-#line 7
-category c147;
-#line 7
-category c148;
-#line 7
-category c149;
-#line 7
-category c150;
-#line 7
-category c151;
-#line 7
-category c152;
-#line 7
-category c153;
-#line 7
-category c154;
-#line 7
-category c155;
-#line 7
-category c156;
-#line 7
-category c157;
-#line 7
-category c158;
-#line 7
-category c159;
-#line 7
-category c160;
-#line 7
-category c161;
-#line 7
-category c162;
-#line 7
-category c163;
-#line 7
-category c164;
-#line 7
-category c165;
-#line 7
-category c166;
-#line 7
-category c167;
-#line 7
-category c168;
-#line 7
-category c169;
-#line 7
-category c170;
-#line 7
-category c171;
-#line 7
-category c172;
-#line 7
-category c173;
-#line 7
-category c174;
-#line 7
-category c175;
-#line 7
-category c176;
-#line 7
-category c177;
-#line 7
-category c178;
-#line 7
-category c179;
-#line 7
-category c180;
-#line 7
-category c181;
-#line 7
-category c182;
-#line 7
-category c183;
-#line 7
-category c184;
-#line 7
-category c185;
-#line 7
-category c186;
-#line 7
-category c187;
-#line 7
-category c188;
-#line 7
-category c189;
-#line 7
-category c190;
-#line 7
-category c191;
-#line 7
-category c192;
-#line 7
-category c193;
-#line 7
-category c194;
-#line 7
-category c195;
-#line 7
-category c196;
-#line 7
-category c197;
-#line 7
-category c198;
-#line 7
-category c199;
-#line 7
-category c200;
-#line 7
-category c201;
-#line 7
-category c202;
-#line 7
-category c203;
-#line 7
-category c204;
-#line 7
-category c205;
-#line 7
-category c206;
-#line 7
-category c207;
-#line 7
-category c208;
-#line 7
-category c209;
-#line 7
-category c210;
-#line 7
-category c211;
-#line 7
-category c212;
-#line 7
-category c213;
-#line 7
-category c214;
-#line 7
-category c215;
-#line 7
-category c216;
-#line 7
-category c217;
-#line 7
-category c218;
-#line 7
-category c219;
-#line 7
-category c220;
-#line 7
-category c221;
-#line 7
-category c222;
-#line 7
-category c223;
-#line 7
-category c224;
-#line 7
-category c225;
-#line 7
-category c226;
-#line 7
-category c227;
-#line 7
-category c228;
-#line 7
-category c229;
-#line 7
-category c230;
-#line 7
-category c231;
-#line 7
-category c232;
-#line 7
-category c233;
-#line 7
-category c234;
-#line 7
-category c235;
-#line 7
-category c236;
-#line 7
-category c237;
-#line 7
-category c238;
-#line 7
-category c239;
-#line 7
-category c240;
-#line 7
-category c241;
-#line 7
-category c242;
-#line 7
-category c243;
-#line 7
-category c244;
-#line 7
-category c245;
-#line 7
-category c246;
-#line 7
-category c247;
-#line 7
-category c248;
-#line 7
-category c249;
-#line 7
-category c250;
-#line 7
-category c251;
-#line 7
-category c252;
-#line 7
-category c253;
-#line 7
-category c254;
-#line 7
-category c255;
-#line 7
-category c256;
-#line 7
-category c257;
-#line 7
-category c258;
-#line 7
-category c259;
-#line 7
-category c260;
-#line 7
-category c261;
-#line 7
-category c262;
-#line 7
-category c263;
-#line 7
-category c264;
-#line 7
-category c265;
-#line 7
-category c266;
-#line 7
-category c267;
-#line 7
-category c268;
-#line 7
-category c269;
-#line 7
-category c270;
-#line 7
-category c271;
-#line 7
-category c272;
-#line 7
-category c273;
-#line 7
-category c274;
-#line 7
-category c275;
-#line 7
-category c276;
-#line 7
-category c277;
-#line 7
-category c278;
-#line 7
-category c279;
-#line 7
-category c280;
-#line 7
-category c281;
-#line 7
-category c282;
-#line 7
-category c283;
-#line 7
-category c284;
-#line 7
-category c285;
-#line 7
-category c286;
-#line 7
-category c287;
-#line 7
-category c288;
-#line 7
-category c289;
-#line 7
-category c290;
-#line 7
-category c291;
-#line 7
-category c292;
-#line 7
-category c293;
-#line 7
-category c294;
-#line 7
-category c295;
-#line 7
-category c296;
-#line 7
-category c297;
-#line 7
-category c298;
-#line 7
-category c299;
-#line 7
-category c300;
-#line 7
-category c301;
-#line 7
-category c302;
-#line 7
-category c303;
-#line 7
-category c304;
-#line 7
-category c305;
-#line 7
-category c306;
-#line 7
-category c307;
-#line 7
-category c308;
-#line 7
-category c309;
-#line 7
-category c310;
-#line 7
-category c311;
-#line 7
-category c312;
-#line 7
-category c313;
-#line 7
-category c314;
-#line 7
-category c315;
-#line 7
-category c316;
-#line 7
-category c317;
-#line 7
-category c318;
-#line 7
-category c319;
-#line 7
-category c320;
-#line 7
-category c321;
-#line 7
-category c322;
-#line 7
-category c323;
-#line 7
-category c324;
-#line 7
-category c325;
-#line 7
-category c326;
-#line 7
-category c327;
-#line 7
-category c328;
-#line 7
-category c329;
-#line 7
-category c330;
-#line 7
-category c331;
-#line 7
-category c332;
-#line 7
-category c333;
-#line 7
-category c334;
-#line 7
-category c335;
-#line 7
-category c336;
-#line 7
-category c337;
-#line 7
-category c338;
-#line 7
-category c339;
-#line 7
-category c340;
-#line 7
-category c341;
-#line 7
-category c342;
-#line 7
-category c343;
-#line 7
-category c344;
-#line 7
-category c345;
-#line 7
-category c346;
-#line 7
-category c347;
-#line 7
-category c348;
-#line 7
-category c349;
-#line 7
-category c350;
-#line 7
-category c351;
-#line 7
-category c352;
-#line 7
-category c353;
-#line 7
-category c354;
-#line 7
-category c355;
-#line 7
-category c356;
-#line 7
-category c357;
-#line 7
-category c358;
-#line 7
-category c359;
-#line 7
-category c360;
-#line 7
-category c361;
-#line 7
-category c362;
-#line 7
-category c363;
-#line 7
-category c364;
-#line 7
-category c365;
-#line 7
-category c366;
-#line 7
-category c367;
-#line 7
-category c368;
-#line 7
-category c369;
-#line 7
-category c370;
-#line 7
-category c371;
-#line 7
-category c372;
-#line 7
-category c373;
-#line 7
-category c374;
-#line 7
-category c375;
-#line 7
-category c376;
-#line 7
-category c377;
-#line 7
-category c378;
-#line 7
-category c379;
-#line 7
-category c380;
-#line 7
-category c381;
-#line 7
-category c382;
-#line 7
-category c383;
-#line 7
-category c384;
-#line 7
-category c385;
-#line 7
-category c386;
-#line 7
-category c387;
-#line 7
-category c388;
-#line 7
-category c389;
-#line 7
-category c390;
-#line 7
-category c391;
-#line 7
-category c392;
-#line 7
-category c393;
-#line 7
-category c394;
-#line 7
-category c395;
-#line 7
-category c396;
-#line 7
-category c397;
-#line 7
-category c398;
-#line 7
-category c399;
-#line 7
-category c400;
-#line 7
-category c401;
-#line 7
-category c402;
-#line 7
-category c403;
-#line 7
-category c404;
-#line 7
-category c405;
-#line 7
-category c406;
-#line 7
-category c407;
-#line 7
-category c408;
-#line 7
-category c409;
-#line 7
-category c410;
-#line 7
-category c411;
-#line 7
-category c412;
-#line 7
-category c413;
-#line 7
-category c414;
-#line 7
-category c415;
-#line 7
-category c416;
-#line 7
-category c417;
-#line 7
-category c418;
-#line 7
-category c419;
-#line 7
-category c420;
-#line 7
-category c421;
-#line 7
-category c422;
-#line 7
-category c423;
-#line 7
-category c424;
-#line 7
-category c425;
-#line 7
-category c426;
-#line 7
-category c427;
-#line 7
-category c428;
-#line 7
-category c429;
-#line 7
-category c430;
-#line 7
-category c431;
-#line 7
-category c432;
-#line 7
-category c433;
-#line 7
-category c434;
-#line 7
-category c435;
-#line 7
-category c436;
-#line 7
-category c437;
-#line 7
-category c438;
-#line 7
-category c439;
-#line 7
-category c440;
-#line 7
-category c441;
-#line 7
-category c442;
-#line 7
-category c443;
-#line 7
-category c444;
-#line 7
-category c445;
-#line 7
-category c446;
-#line 7
-category c447;
-#line 7
-category c448;
-#line 7
-category c449;
-#line 7
-category c450;
-#line 7
-category c451;
-#line 7
-category c452;
-#line 7
-category c453;
-#line 7
-category c454;
-#line 7
-category c455;
-#line 7
-category c456;
-#line 7
-category c457;
-#line 7
-category c458;
-#line 7
-category c459;
-#line 7
-category c460;
-#line 7
-category c461;
-#line 7
-category c462;
-#line 7
-category c463;
-#line 7
-category c464;
-#line 7
-category c465;
-#line 7
-category c466;
-#line 7
-category c467;
-#line 7
-category c468;
-#line 7
-category c469;
-#line 7
-category c470;
-#line 7
-category c471;
-#line 7
-category c472;
-#line 7
-category c473;
-#line 7
-category c474;
-#line 7
-category c475;
-#line 7
-category c476;
-#line 7
-category c477;
-#line 7
-category c478;
-#line 7
-category c479;
-#line 7
-category c480;
-#line 7
-category c481;
-#line 7
-category c482;
-#line 7
-category c483;
-#line 7
-category c484;
-#line 7
-category c485;
-#line 7
-category c486;
-#line 7
-category c487;
-#line 7
-category c488;
-#line 7
-category c489;
-#line 7
-category c490;
-#line 7
-category c491;
-#line 7
-category c492;
-#line 7
-category c493;
-#line 7
-category c494;
-#line 7
-category c495;
-#line 7
-category c496;
-#line 7
-category c497;
-#line 7
-category c498;
-#line 7
-category c499;
-#line 7
-category c500;
-#line 7
-category c501;
-#line 7
-category c502;
-#line 7
-category c503;
-#line 7
-category c504;
-#line 7
-category c505;
-#line 7
-category c506;
-#line 7
-category c507;
-#line 7
-category c508;
-#line 7
-category c509;
-#line 7
-category c510;
-#line 7
-category c511;
-#line 7
-category c512;
-#line 7
-category c513;
-#line 7
-category c514;
-#line 7
-category c515;
-#line 7
-category c516;
-#line 7
-category c517;
-#line 7
-category c518;
-#line 7
-category c519;
-#line 7
-category c520;
-#line 7
-category c521;
-#line 7
-category c522;
-#line 7
-category c523;
-#line 7
-category c524;
-#line 7
-category c525;
-#line 7
-category c526;
-#line 7
-category c527;
-#line 7
-category c528;
-#line 7
-category c529;
-#line 7
-category c530;
-#line 7
-category c531;
-#line 7
-category c532;
-#line 7
-category c533;
-#line 7
-category c534;
-#line 7
-category c535;
-#line 7
-category c536;
-#line 7
-category c537;
-#line 7
-category c538;
-#line 7
-category c539;
-#line 7
-category c540;
-#line 7
-category c541;
-#line 7
-category c542;
-#line 7
-category c543;
-#line 7
-category c544;
-#line 7
-category c545;
-#line 7
-category c546;
-#line 7
-category c547;
-#line 7
-category c548;
-#line 7
-category c549;
-#line 7
-category c550;
-#line 7
-category c551;
-#line 7
-category c552;
-#line 7
-category c553;
-#line 7
-category c554;
-#line 7
-category c555;
-#line 7
-category c556;
-#line 7
-category c557;
-#line 7
-category c558;
-#line 7
-category c559;
-#line 7
-category c560;
-#line 7
-category c561;
-#line 7
-category c562;
-#line 7
-category c563;
-#line 7
-category c564;
-#line 7
-category c565;
-#line 7
-category c566;
-#line 7
-category c567;
-#line 7
-category c568;
-#line 7
-category c569;
-#line 7
-category c570;
-#line 7
-category c571;
-#line 7
-category c572;
-#line 7
-category c573;
-#line 7
-category c574;
-#line 7
-category c575;
-#line 7
-category c576;
-#line 7
-category c577;
-#line 7
-category c578;
-#line 7
-category c579;
-#line 7
-category c580;
-#line 7
-category c581;
-#line 7
-category c582;
-#line 7
-category c583;
-#line 7
-category c584;
-#line 7
-category c585;
-#line 7
-category c586;
-#line 7
-category c587;
-#line 7
-category c588;
-#line 7
-category c589;
-#line 7
-category c590;
-#line 7
-category c591;
-#line 7
-category c592;
-#line 7
-category c593;
-#line 7
-category c594;
-#line 7
-category c595;
-#line 7
-category c596;
-#line 7
-category c597;
-#line 7
-category c598;
-#line 7
-category c599;
-#line 7
-category c600;
-#line 7
-category c601;
-#line 7
-category c602;
-#line 7
-category c603;
-#line 7
-category c604;
-#line 7
-category c605;
-#line 7
-category c606;
-#line 7
-category c607;
-#line 7
-category c608;
-#line 7
-category c609;
-#line 7
-category c610;
-#line 7
-category c611;
-#line 7
-category c612;
-#line 7
-category c613;
-#line 7
-category c614;
-#line 7
-category c615;
-#line 7
-category c616;
-#line 7
-category c617;
-#line 7
-category c618;
-#line 7
-category c619;
-#line 7
-category c620;
-#line 7
-category c621;
-#line 7
-category c622;
-#line 7
-category c623;
-#line 7
-category c624;
-#line 7
-category c625;
-#line 7
-category c626;
-#line 7
-category c627;
-#line 7
-category c628;
-#line 7
-category c629;
-#line 7
-category c630;
-#line 7
-category c631;
-#line 7
-category c632;
-#line 7
-category c633;
-#line 7
-category c634;
-#line 7
-category c635;
-#line 7
-category c636;
-#line 7
-category c637;
-#line 7
-category c638;
-#line 7
-category c639;
-#line 7
-category c640;
-#line 7
-category c641;
-#line 7
-category c642;
-#line 7
-category c643;
-#line 7
-category c644;
-#line 7
-category c645;
-#line 7
-category c646;
-#line 7
-category c647;
-#line 7
-category c648;
-#line 7
-category c649;
-#line 7
-category c650;
-#line 7
-category c651;
-#line 7
-category c652;
-#line 7
-category c653;
-#line 7
-category c654;
-#line 7
-category c655;
-#line 7
-category c656;
-#line 7
-category c657;
-#line 7
-category c658;
-#line 7
-category c659;
-#line 7
-category c660;
-#line 7
-category c661;
-#line 7
-category c662;
-#line 7
-category c663;
-#line 7
-category c664;
-#line 7
-category c665;
-#line 7
-category c666;
-#line 7
-category c667;
-#line 7
-category c668;
-#line 7
-category c669;
-#line 7
-category c670;
-#line 7
-category c671;
-#line 7
-category c672;
-#line 7
-category c673;
-#line 7
-category c674;
-#line 7
-category c675;
-#line 7
-category c676;
-#line 7
-category c677;
-#line 7
-category c678;
-#line 7
-category c679;
-#line 7
-category c680;
-#line 7
-category c681;
-#line 7
-category c682;
-#line 7
-category c683;
-#line 7
-category c684;
-#line 7
-category c685;
-#line 7
-category c686;
-#line 7
-category c687;
-#line 7
-category c688;
-#line 7
-category c689;
-#line 7
-category c690;
-#line 7
-category c691;
-#line 7
-category c692;
-#line 7
-category c693;
-#line 7
-category c694;
-#line 7
-category c695;
-#line 7
-category c696;
-#line 7
-category c697;
-#line 7
-category c698;
-#line 7
-category c699;
-#line 7
-category c700;
-#line 7
-category c701;
-#line 7
-category c702;
-#line 7
-category c703;
-#line 7
-category c704;
-#line 7
-category c705;
-#line 7
-category c706;
-#line 7
-category c707;
-#line 7
-category c708;
-#line 7
-category c709;
-#line 7
-category c710;
-#line 7
-category c711;
-#line 7
-category c712;
-#line 7
-category c713;
-#line 7
-category c714;
-#line 7
-category c715;
-#line 7
-category c716;
-#line 7
-category c717;
-#line 7
-category c718;
-#line 7
-category c719;
-#line 7
-category c720;
-#line 7
-category c721;
-#line 7
-category c722;
-#line 7
-category c723;
-#line 7
-category c724;
-#line 7
-category c725;
-#line 7
-category c726;
-#line 7
-category c727;
-#line 7
-category c728;
-#line 7
-category c729;
-#line 7
-category c730;
-#line 7
-category c731;
-#line 7
-category c732;
-#line 7
-category c733;
-#line 7
-category c734;
-#line 7
-category c735;
-#line 7
-category c736;
-#line 7
-category c737;
-#line 7
-category c738;
-#line 7
-category c739;
-#line 7
-category c740;
-#line 7
-category c741;
-#line 7
-category c742;
-#line 7
-category c743;
-#line 7
-category c744;
-#line 7
-category c745;
-#line 7
-category c746;
-#line 7
-category c747;
-#line 7
-category c748;
-#line 7
-category c749;
-#line 7
-category c750;
-#line 7
-category c751;
-#line 7
-category c752;
-#line 7
-category c753;
-#line 7
-category c754;
-#line 7
-category c755;
-#line 7
-category c756;
-#line 7
-category c757;
-#line 7
-category c758;
-#line 7
-category c759;
-#line 7
-category c760;
-#line 7
-category c761;
-#line 7
-category c762;
-#line 7
-category c763;
-#line 7
-category c764;
-#line 7
-category c765;
-#line 7
-category c766;
-#line 7
-category c767;
-#line 7
-category c768;
-#line 7
-category c769;
-#line 7
-category c770;
-#line 7
-category c771;
-#line 7
-category c772;
-#line 7
-category c773;
-#line 7
-category c774;
-#line 7
-category c775;
-#line 7
-category c776;
-#line 7
-category c777;
-#line 7
-category c778;
-#line 7
-category c779;
-#line 7
-category c780;
-#line 7
-category c781;
-#line 7
-category c782;
-#line 7
-category c783;
-#line 7
-category c784;
-#line 7
-category c785;
-#line 7
-category c786;
-#line 7
-category c787;
-#line 7
-category c788;
-#line 7
-category c789;
-#line 7
-category c790;
-#line 7
-category c791;
-#line 7
-category c792;
-#line 7
-category c793;
-#line 7
-category c794;
-#line 7
-category c795;
-#line 7
-category c796;
-#line 7
-category c797;
-#line 7
-category c798;
-#line 7
-category c799;
-#line 7
-category c800;
-#line 7
-category c801;
-#line 7
-category c802;
-#line 7
-category c803;
-#line 7
-category c804;
-#line 7
-category c805;
-#line 7
-category c806;
-#line 7
-category c807;
-#line 7
-category c808;
-#line 7
-category c809;
-#line 7
-category c810;
-#line 7
-category c811;
-#line 7
-category c812;
-#line 7
-category c813;
-#line 7
-category c814;
-#line 7
-category c815;
-#line 7
-category c816;
-#line 7
-category c817;
-#line 7
-category c818;
-#line 7
-category c819;
-#line 7
-category c820;
-#line 7
-category c821;
-#line 7
-category c822;
-#line 7
-category c823;
-#line 7
-category c824;
-#line 7
-category c825;
-#line 7
-category c826;
-#line 7
-category c827;
-#line 7
-category c828;
-#line 7
-category c829;
-#line 7
-category c830;
-#line 7
-category c831;
-#line 7
-category c832;
-#line 7
-category c833;
-#line 7
-category c834;
-#line 7
-category c835;
-#line 7
-category c836;
-#line 7
-category c837;
-#line 7
-category c838;
-#line 7
-category c839;
-#line 7
-category c840;
-#line 7
-category c841;
-#line 7
-category c842;
-#line 7
-category c843;
-#line 7
-category c844;
-#line 7
-category c845;
-#line 7
-category c846;
-#line 7
-category c847;
-#line 7
-category c848;
-#line 7
-category c849;
-#line 7
-category c850;
-#line 7
-category c851;
-#line 7
-category c852;
-#line 7
-category c853;
-#line 7
-category c854;
-#line 7
-category c855;
-#line 7
-category c856;
-#line 7
-category c857;
-#line 7
-category c858;
-#line 7
-category c859;
-#line 7
-category c860;
-#line 7
-category c861;
-#line 7
-category c862;
-#line 7
-category c863;
-#line 7
-category c864;
-#line 7
-category c865;
-#line 7
-category c866;
-#line 7
-category c867;
-#line 7
-category c868;
-#line 7
-category c869;
-#line 7
-category c870;
-#line 7
-category c871;
-#line 7
-category c872;
-#line 7
-category c873;
-#line 7
-category c874;
-#line 7
-category c875;
-#line 7
-category c876;
-#line 7
-category c877;
-#line 7
-category c878;
-#line 7
-category c879;
-#line 7
-category c880;
-#line 7
-category c881;
-#line 7
-category c882;
-#line 7
-category c883;
-#line 7
-category c884;
-#line 7
-category c885;
-#line 7
-category c886;
-#line 7
-category c887;
-#line 7
-category c888;
-#line 7
-category c889;
-#line 7
-category c890;
-#line 7
-category c891;
-#line 7
-category c892;
-#line 7
-category c893;
-#line 7
-category c894;
-#line 7
-category c895;
-#line 7
-category c896;
-#line 7
-category c897;
-#line 7
-category c898;
-#line 7
-category c899;
-#line 7
-category c900;
-#line 7
-category c901;
-#line 7
-category c902;
-#line 7
-category c903;
-#line 7
-category c904;
-#line 7
-category c905;
-#line 7
-category c906;
-#line 7
-category c907;
-#line 7
-category c908;
-#line 7
-category c909;
-#line 7
-category c910;
-#line 7
-category c911;
-#line 7
-category c912;
-#line 7
-category c913;
-#line 7
-category c914;
-#line 7
-category c915;
-#line 7
-category c916;
-#line 7
-category c917;
-#line 7
-category c918;
-#line 7
-category c919;
-#line 7
-category c920;
-#line 7
-category c921;
-#line 7
-category c922;
-#line 7
-category c923;
-#line 7
-category c924;
-#line 7
-category c925;
-#line 7
-category c926;
-#line 7
-category c927;
-#line 7
-category c928;
-#line 7
-category c929;
-#line 7
-category c930;
-#line 7
-category c931;
-#line 7
-category c932;
-#line 7
-category c933;
-#line 7
-category c934;
-#line 7
-category c935;
-#line 7
-category c936;
-#line 7
-category c937;
-#line 7
-category c938;
-#line 7
-category c939;
-#line 7
-category c940;
-#line 7
-category c941;
-#line 7
-category c942;
-#line 7
-category c943;
-#line 7
-category c944;
-#line 7
-category c945;
-#line 7
-category c946;
-#line 7
-category c947;
-#line 7
-category c948;
-#line 7
-category c949;
-#line 7
-category c950;
-#line 7
-category c951;
-#line 7
-category c952;
-#line 7
-category c953;
-#line 7
-category c954;
-#line 7
-category c955;
-#line 7
-category c956;
-#line 7
-category c957;
-#line 7
-category c958;
-#line 7
-category c959;
-#line 7
-category c960;
-#line 7
-category c961;
-#line 7
-category c962;
-#line 7
-category c963;
-#line 7
-category c964;
-#line 7
-category c965;
-#line 7
-category c966;
-#line 7
-category c967;
-#line 7
-category c968;
-#line 7
-category c969;
-#line 7
-category c970;
-#line 7
-category c971;
-#line 7
-category c972;
-#line 7
-category c973;
-#line 7
-category c974;
-#line 7
-category c975;
-#line 7
-category c976;
-#line 7
-category c977;
-#line 7
-category c978;
-#line 7
-category c979;
-#line 7
-category c980;
-#line 7
-category c981;
-#line 7
-category c982;
-#line 7
-category c983;
-#line 7
-category c984;
-#line 7
-category c985;
-#line 7
-category c986;
-#line 7
-category c987;
-#line 7
-category c988;
-#line 7
-category c989;
-#line 7
-category c990;
-#line 7
-category c991;
-#line 7
-category c992;
-#line 7
-category c993;
-#line 7
-category c994;
-#line 7
-category c995;
-#line 7
-category c996;
-#line 7
-category c997;
-#line 7
-category c998;
-#line 7
-category c999;
-#line 7
-category c1000;
-#line 7
-category c1001;
-#line 7
-category c1002;
-#line 7
-category c1003;
-#line 7
-category c1004;
-#line 7
-category c1005;
-#line 7
-category c1006;
-#line 7
-category c1007;
-#line 7
-category c1008;
-#line 7
-category c1009;
-#line 7
-category c1010;
-#line 7
-category c1011;
-#line 7
-category c1012;
-#line 7
-category c1013;
-#line 7
-category c1014;
-#line 7
-category c1015;
-#line 7
-category c1016;
-#line 7
-category c1017;
-#line 7
-category c1018;
-#line 7
-category c1019;
-#line 7
-category c1020;
-#line 7
-category c1021;
-#line 7
-category c1022;
-#line 7
-category c1023;
-#line 7
-
-
-# Generate level definitions for each sensitivity and category.
-level s0:c0.c1023;
-#line 10
-
-######################################
-# Attribute declarations
-#
-
-# All types used for processes.
-attribute domain;
-
-# Domains that are allowed all permissions ("unconfined").
-attribute unconfineddomain;
-
-# All domains used for apps.
-attribute appdomain;
-
-# All types used for files that can exist on a labeled fs.
-# Do not use for pseudo file types.
-attribute file_type;
-
-# All types used for domain entry points.
-attribute exec_type;
-
-#line 1 "external/sepolicy/bluetooth.te"
-# bluetooth subsystem
-type bluetooth, domain;
-permissive bluetooth;
-
-#line 4
-typeattribute bluetooth appdomain;
-
-#line 5
-typeattribute bluetooth unconfineddomain;
-#line 5
-
-#line 1 "external/sepolicy/healthd.te"
-# healthd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type healthd, domain;
-permissive healthd;
-type healthd_exec, exec_type, file_type;
-
-# New domain is entered by executing the file.
-#line 7
-allow healthd healthd_exec:file { entrypoint read execute };
-
-###
-### Neverallow rules
-###
-### These are things that Android apps should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-
-# Added to make the neverallow rule make sense in a limited environment.
-# Added at the bottom to not throw off file seek numbers in test suite.  
-# This is not a problem, because allow rules are processed after all types
-# are gathered.
-type testTYPE, appdomain, domain;
diff --git a/tools/selinux/test/policy_test.conf b/tools/selinux/test/policy_test.conf
deleted file mode 100644
index d0962cd..0000000
--- a/tools/selinux/test/policy_test.conf
+++ /dev/null
@@ -1,2244 +0,0 @@
-#line 1 "external/sepolicy/security_classes"
-# FLASK
-
-#
-# Define the security object classes
-#
-
-# Classes marked as userspace are classes
-# for userspace object managers
-
-class capability
-
-# file-related classes
-class file
-
-#
-# Define a common prefix for file access vectors.
-#
-
-common file
-{
-	ioctl
-	read
-	write
-	create
-	getattr
-	setattr
-	lock
-	relabelfrom
-	relabelto
-	append
-	unlink
-	link
-	rename
-	execute
-	swapon
-	quotaon
-	mounton
-}
-
-class file
-inherits file
-{
-	execute_no_trans
-	entrypoint
-	execmod
-	open
-	audit_access
-}
-
-class capability
-{
-	# The capabilities are defined in include/linux/capability.h
-	# Capabilities >= 32 are defined in the capability2 class.
-	# Care should be taken to ensure that these are consistent with
-	# those definitions. (Order matters)
-
-	chown
-	dac_override
-	dac_read_search
-	fowner
-	fsetid
-	kill
-	setgid
-	setuid
-	setpcap
-	linux_immutable
-	net_bind_service
-	net_broadcast
-	net_admin
-	net_raw
-	ipc_lock
-	ipc_owner
-	sys_module
-	sys_rawio
-	sys_chroot
-	sys_ptrace
-	sys_pacct
-	sys_admin
-	sys_boot
-	sys_nice
-	sys_resource
-	sys_time
-	sys_tty_config
-	mknod
-	lease
-	audit_write
-	audit_control
-	setfcap
-}
-
-########################################
-#
-# Basic level names for system low and high
-#
-
-
-#line 1 "external/sepolicy/mls"
-#########################################
-# MLS declarations
-#
-
-# Generate the desired number of sensitivities and categories.
-
-#line 6
-# Each sensitivity has a name and zero or more aliases.
-#line 6
-sensitivity s0;
-#line 6
-
-#line 6
-
-#line 6
-# Define the ordering of the sensitivity levels (least to greatest)
-#line 6
-dominance { s0  }
-#line 6
-
-category c0;
-#line 7
-category c1;
-#line 7
-category c2;
-#line 7
-category c3;
-#line 7
-category c4;
-#line 7
-category c5;
-#line 7
-category c6;
-#line 7
-category c7;
-#line 7
-category c8;
-#line 7
-category c9;
-#line 7
-category c10;
-#line 7
-category c11;
-#line 7
-category c12;
-#line 7
-category c13;
-#line 7
-category c14;
-#line 7
-category c15;
-#line 7
-category c16;
-#line 7
-category c17;
-#line 7
-category c18;
-#line 7
-category c19;
-#line 7
-category c20;
-#line 7
-category c21;
-#line 7
-category c22;
-#line 7
-category c23;
-#line 7
-category c24;
-#line 7
-category c25;
-#line 7
-category c26;
-#line 7
-category c27;
-#line 7
-category c28;
-#line 7
-category c29;
-#line 7
-category c30;
-#line 7
-category c31;
-#line 7
-category c32;
-#line 7
-category c33;
-#line 7
-category c34;
-#line 7
-category c35;
-#line 7
-category c36;
-#line 7
-category c37;
-#line 7
-category c38;
-#line 7
-category c39;
-#line 7
-category c40;
-#line 7
-category c41;
-#line 7
-category c42;
-#line 7
-category c43;
-#line 7
-category c44;
-#line 7
-category c45;
-#line 7
-category c46;
-#line 7
-category c47;
-#line 7
-category c48;
-#line 7
-category c49;
-#line 7
-category c50;
-#line 7
-category c51;
-#line 7
-category c52;
-#line 7
-category c53;
-#line 7
-category c54;
-#line 7
-category c55;
-#line 7
-category c56;
-#line 7
-category c57;
-#line 7
-category c58;
-#line 7
-category c59;
-#line 7
-category c60;
-#line 7
-category c61;
-#line 7
-category c62;
-#line 7
-category c63;
-#line 7
-category c64;
-#line 7
-category c65;
-#line 7
-category c66;
-#line 7
-category c67;
-#line 7
-category c68;
-#line 7
-category c69;
-#line 7
-category c70;
-#line 7
-category c71;
-#line 7
-category c72;
-#line 7
-category c73;
-#line 7
-category c74;
-#line 7
-category c75;
-#line 7
-category c76;
-#line 7
-category c77;
-#line 7
-category c78;
-#line 7
-category c79;
-#line 7
-category c80;
-#line 7
-category c81;
-#line 7
-category c82;
-#line 7
-category c83;
-#line 7
-category c84;
-#line 7
-category c85;
-#line 7
-category c86;
-#line 7
-category c87;
-#line 7
-category c88;
-#line 7
-category c89;
-#line 7
-category c90;
-#line 7
-category c91;
-#line 7
-category c92;
-#line 7
-category c93;
-#line 7
-category c94;
-#line 7
-category c95;
-#line 7
-category c96;
-#line 7
-category c97;
-#line 7
-category c98;
-#line 7
-category c99;
-#line 7
-category c100;
-#line 7
-category c101;
-#line 7
-category c102;
-#line 7
-category c103;
-#line 7
-category c104;
-#line 7
-category c105;
-#line 7
-category c106;
-#line 7
-category c107;
-#line 7
-category c108;
-#line 7
-category c109;
-#line 7
-category c110;
-#line 7
-category c111;
-#line 7
-category c112;
-#line 7
-category c113;
-#line 7
-category c114;
-#line 7
-category c115;
-#line 7
-category c116;
-#line 7
-category c117;
-#line 7
-category c118;
-#line 7
-category c119;
-#line 7
-category c120;
-#line 7
-category c121;
-#line 7
-category c122;
-#line 7
-category c123;
-#line 7
-category c124;
-#line 7
-category c125;
-#line 7
-category c126;
-#line 7
-category c127;
-#line 7
-category c128;
-#line 7
-category c129;
-#line 7
-category c130;
-#line 7
-category c131;
-#line 7
-category c132;
-#line 7
-category c133;
-#line 7
-category c134;
-#line 7
-category c135;
-#line 7
-category c136;
-#line 7
-category c137;
-#line 7
-category c138;
-#line 7
-category c139;
-#line 7
-category c140;
-#line 7
-category c141;
-#line 7
-category c142;
-#line 7
-category c143;
-#line 7
-category c144;
-#line 7
-category c145;
-#line 7
-category c146;
-#line 7
-category c147;
-#line 7
-category c148;
-#line 7
-category c149;
-#line 7
-category c150;
-#line 7
-category c151;
-#line 7
-category c152;
-#line 7
-category c153;
-#line 7
-category c154;
-#line 7
-category c155;
-#line 7
-category c156;
-#line 7
-category c157;
-#line 7
-category c158;
-#line 7
-category c159;
-#line 7
-category c160;
-#line 7
-category c161;
-#line 7
-category c162;
-#line 7
-category c163;
-#line 7
-category c164;
-#line 7
-category c165;
-#line 7
-category c166;
-#line 7
-category c167;
-#line 7
-category c168;
-#line 7
-category c169;
-#line 7
-category c170;
-#line 7
-category c171;
-#line 7
-category c172;
-#line 7
-category c173;
-#line 7
-category c174;
-#line 7
-category c175;
-#line 7
-category c176;
-#line 7
-category c177;
-#line 7
-category c178;
-#line 7
-category c179;
-#line 7
-category c180;
-#line 7
-category c181;
-#line 7
-category c182;
-#line 7
-category c183;
-#line 7
-category c184;
-#line 7
-category c185;
-#line 7
-category c186;
-#line 7
-category c187;
-#line 7
-category c188;
-#line 7
-category c189;
-#line 7
-category c190;
-#line 7
-category c191;
-#line 7
-category c192;
-#line 7
-category c193;
-#line 7
-category c194;
-#line 7
-category c195;
-#line 7
-category c196;
-#line 7
-category c197;
-#line 7
-category c198;
-#line 7
-category c199;
-#line 7
-category c200;
-#line 7
-category c201;
-#line 7
-category c202;
-#line 7
-category c203;
-#line 7
-category c204;
-#line 7
-category c205;
-#line 7
-category c206;
-#line 7
-category c207;
-#line 7
-category c208;
-#line 7
-category c209;
-#line 7
-category c210;
-#line 7
-category c211;
-#line 7
-category c212;
-#line 7
-category c213;
-#line 7
-category c214;
-#line 7
-category c215;
-#line 7
-category c216;
-#line 7
-category c217;
-#line 7
-category c218;
-#line 7
-category c219;
-#line 7
-category c220;
-#line 7
-category c221;
-#line 7
-category c222;
-#line 7
-category c223;
-#line 7
-category c224;
-#line 7
-category c225;
-#line 7
-category c226;
-#line 7
-category c227;
-#line 7
-category c228;
-#line 7
-category c229;
-#line 7
-category c230;
-#line 7
-category c231;
-#line 7
-category c232;
-#line 7
-category c233;
-#line 7
-category c234;
-#line 7
-category c235;
-#line 7
-category c236;
-#line 7
-category c237;
-#line 7
-category c238;
-#line 7
-category c239;
-#line 7
-category c240;
-#line 7
-category c241;
-#line 7
-category c242;
-#line 7
-category c243;
-#line 7
-category c244;
-#line 7
-category c245;
-#line 7
-category c246;
-#line 7
-category c247;
-#line 7
-category c248;
-#line 7
-category c249;
-#line 7
-category c250;
-#line 7
-category c251;
-#line 7
-category c252;
-#line 7
-category c253;
-#line 7
-category c254;
-#line 7
-category c255;
-#line 7
-category c256;
-#line 7
-category c257;
-#line 7
-category c258;
-#line 7
-category c259;
-#line 7
-category c260;
-#line 7
-category c261;
-#line 7
-category c262;
-#line 7
-category c263;
-#line 7
-category c264;
-#line 7
-category c265;
-#line 7
-category c266;
-#line 7
-category c267;
-#line 7
-category c268;
-#line 7
-category c269;
-#line 7
-category c270;
-#line 7
-category c271;
-#line 7
-category c272;
-#line 7
-category c273;
-#line 7
-category c274;
-#line 7
-category c275;
-#line 7
-category c276;
-#line 7
-category c277;
-#line 7
-category c278;
-#line 7
-category c279;
-#line 7
-category c280;
-#line 7
-category c281;
-#line 7
-category c282;
-#line 7
-category c283;
-#line 7
-category c284;
-#line 7
-category c285;
-#line 7
-category c286;
-#line 7
-category c287;
-#line 7
-category c288;
-#line 7
-category c289;
-#line 7
-category c290;
-#line 7
-category c291;
-#line 7
-category c292;
-#line 7
-category c293;
-#line 7
-category c294;
-#line 7
-category c295;
-#line 7
-category c296;
-#line 7
-category c297;
-#line 7
-category c298;
-#line 7
-category c299;
-#line 7
-category c300;
-#line 7
-category c301;
-#line 7
-category c302;
-#line 7
-category c303;
-#line 7
-category c304;
-#line 7
-category c305;
-#line 7
-category c306;
-#line 7
-category c307;
-#line 7
-category c308;
-#line 7
-category c309;
-#line 7
-category c310;
-#line 7
-category c311;
-#line 7
-category c312;
-#line 7
-category c313;
-#line 7
-category c314;
-#line 7
-category c315;
-#line 7
-category c316;
-#line 7
-category c317;
-#line 7
-category c318;
-#line 7
-category c319;
-#line 7
-category c320;
-#line 7
-category c321;
-#line 7
-category c322;
-#line 7
-category c323;
-#line 7
-category c324;
-#line 7
-category c325;
-#line 7
-category c326;
-#line 7
-category c327;
-#line 7
-category c328;
-#line 7
-category c329;
-#line 7
-category c330;
-#line 7
-category c331;
-#line 7
-category c332;
-#line 7
-category c333;
-#line 7
-category c334;
-#line 7
-category c335;
-#line 7
-category c336;
-#line 7
-category c337;
-#line 7
-category c338;
-#line 7
-category c339;
-#line 7
-category c340;
-#line 7
-category c341;
-#line 7
-category c342;
-#line 7
-category c343;
-#line 7
-category c344;
-#line 7
-category c345;
-#line 7
-category c346;
-#line 7
-category c347;
-#line 7
-category c348;
-#line 7
-category c349;
-#line 7
-category c350;
-#line 7
-category c351;
-#line 7
-category c352;
-#line 7
-category c353;
-#line 7
-category c354;
-#line 7
-category c355;
-#line 7
-category c356;
-#line 7
-category c357;
-#line 7
-category c358;
-#line 7
-category c359;
-#line 7
-category c360;
-#line 7
-category c361;
-#line 7
-category c362;
-#line 7
-category c363;
-#line 7
-category c364;
-#line 7
-category c365;
-#line 7
-category c366;
-#line 7
-category c367;
-#line 7
-category c368;
-#line 7
-category c369;
-#line 7
-category c370;
-#line 7
-category c371;
-#line 7
-category c372;
-#line 7
-category c373;
-#line 7
-category c374;
-#line 7
-category c375;
-#line 7
-category c376;
-#line 7
-category c377;
-#line 7
-category c378;
-#line 7
-category c379;
-#line 7
-category c380;
-#line 7
-category c381;
-#line 7
-category c382;
-#line 7
-category c383;
-#line 7
-category c384;
-#line 7
-category c385;
-#line 7
-category c386;
-#line 7
-category c387;
-#line 7
-category c388;
-#line 7
-category c389;
-#line 7
-category c390;
-#line 7
-category c391;
-#line 7
-category c392;
-#line 7
-category c393;
-#line 7
-category c394;
-#line 7
-category c395;
-#line 7
-category c396;
-#line 7
-category c397;
-#line 7
-category c398;
-#line 7
-category c399;
-#line 7
-category c400;
-#line 7
-category c401;
-#line 7
-category c402;
-#line 7
-category c403;
-#line 7
-category c404;
-#line 7
-category c405;
-#line 7
-category c406;
-#line 7
-category c407;
-#line 7
-category c408;
-#line 7
-category c409;
-#line 7
-category c410;
-#line 7
-category c411;
-#line 7
-category c412;
-#line 7
-category c413;
-#line 7
-category c414;
-#line 7
-category c415;
-#line 7
-category c416;
-#line 7
-category c417;
-#line 7
-category c418;
-#line 7
-category c419;
-#line 7
-category c420;
-#line 7
-category c421;
-#line 7
-category c422;
-#line 7
-category c423;
-#line 7
-category c424;
-#line 7
-category c425;
-#line 7
-category c426;
-#line 7
-category c427;
-#line 7
-category c428;
-#line 7
-category c429;
-#line 7
-category c430;
-#line 7
-category c431;
-#line 7
-category c432;
-#line 7
-category c433;
-#line 7
-category c434;
-#line 7
-category c435;
-#line 7
-category c436;
-#line 7
-category c437;
-#line 7
-category c438;
-#line 7
-category c439;
-#line 7
-category c440;
-#line 7
-category c441;
-#line 7
-category c442;
-#line 7
-category c443;
-#line 7
-category c444;
-#line 7
-category c445;
-#line 7
-category c446;
-#line 7
-category c447;
-#line 7
-category c448;
-#line 7
-category c449;
-#line 7
-category c450;
-#line 7
-category c451;
-#line 7
-category c452;
-#line 7
-category c453;
-#line 7
-category c454;
-#line 7
-category c455;
-#line 7
-category c456;
-#line 7
-category c457;
-#line 7
-category c458;
-#line 7
-category c459;
-#line 7
-category c460;
-#line 7
-category c461;
-#line 7
-category c462;
-#line 7
-category c463;
-#line 7
-category c464;
-#line 7
-category c465;
-#line 7
-category c466;
-#line 7
-category c467;
-#line 7
-category c468;
-#line 7
-category c469;
-#line 7
-category c470;
-#line 7
-category c471;
-#line 7
-category c472;
-#line 7
-category c473;
-#line 7
-category c474;
-#line 7
-category c475;
-#line 7
-category c476;
-#line 7
-category c477;
-#line 7
-category c478;
-#line 7
-category c479;
-#line 7
-category c480;
-#line 7
-category c481;
-#line 7
-category c482;
-#line 7
-category c483;
-#line 7
-category c484;
-#line 7
-category c485;
-#line 7
-category c486;
-#line 7
-category c487;
-#line 7
-category c488;
-#line 7
-category c489;
-#line 7
-category c490;
-#line 7
-category c491;
-#line 7
-category c492;
-#line 7
-category c493;
-#line 7
-category c494;
-#line 7
-category c495;
-#line 7
-category c496;
-#line 7
-category c497;
-#line 7
-category c498;
-#line 7
-category c499;
-#line 7
-category c500;
-#line 7
-category c501;
-#line 7
-category c502;
-#line 7
-category c503;
-#line 7
-category c504;
-#line 7
-category c505;
-#line 7
-category c506;
-#line 7
-category c507;
-#line 7
-category c508;
-#line 7
-category c509;
-#line 7
-category c510;
-#line 7
-category c511;
-#line 7
-category c512;
-#line 7
-category c513;
-#line 7
-category c514;
-#line 7
-category c515;
-#line 7
-category c516;
-#line 7
-category c517;
-#line 7
-category c518;
-#line 7
-category c519;
-#line 7
-category c520;
-#line 7
-category c521;
-#line 7
-category c522;
-#line 7
-category c523;
-#line 7
-category c524;
-#line 7
-category c525;
-#line 7
-category c526;
-#line 7
-category c527;
-#line 7
-category c528;
-#line 7
-category c529;
-#line 7
-category c530;
-#line 7
-category c531;
-#line 7
-category c532;
-#line 7
-category c533;
-#line 7
-category c534;
-#line 7
-category c535;
-#line 7
-category c536;
-#line 7
-category c537;
-#line 7
-category c538;
-#line 7
-category c539;
-#line 7
-category c540;
-#line 7
-category c541;
-#line 7
-category c542;
-#line 7
-category c543;
-#line 7
-category c544;
-#line 7
-category c545;
-#line 7
-category c546;
-#line 7
-category c547;
-#line 7
-category c548;
-#line 7
-category c549;
-#line 7
-category c550;
-#line 7
-category c551;
-#line 7
-category c552;
-#line 7
-category c553;
-#line 7
-category c554;
-#line 7
-category c555;
-#line 7
-category c556;
-#line 7
-category c557;
-#line 7
-category c558;
-#line 7
-category c559;
-#line 7
-category c560;
-#line 7
-category c561;
-#line 7
-category c562;
-#line 7
-category c563;
-#line 7
-category c564;
-#line 7
-category c565;
-#line 7
-category c566;
-#line 7
-category c567;
-#line 7
-category c568;
-#line 7
-category c569;
-#line 7
-category c570;
-#line 7
-category c571;
-#line 7
-category c572;
-#line 7
-category c573;
-#line 7
-category c574;
-#line 7
-category c575;
-#line 7
-category c576;
-#line 7
-category c577;
-#line 7
-category c578;
-#line 7
-category c579;
-#line 7
-category c580;
-#line 7
-category c581;
-#line 7
-category c582;
-#line 7
-category c583;
-#line 7
-category c584;
-#line 7
-category c585;
-#line 7
-category c586;
-#line 7
-category c587;
-#line 7
-category c588;
-#line 7
-category c589;
-#line 7
-category c590;
-#line 7
-category c591;
-#line 7
-category c592;
-#line 7
-category c593;
-#line 7
-category c594;
-#line 7
-category c595;
-#line 7
-category c596;
-#line 7
-category c597;
-#line 7
-category c598;
-#line 7
-category c599;
-#line 7
-category c600;
-#line 7
-category c601;
-#line 7
-category c602;
-#line 7
-category c603;
-#line 7
-category c604;
-#line 7
-category c605;
-#line 7
-category c606;
-#line 7
-category c607;
-#line 7
-category c608;
-#line 7
-category c609;
-#line 7
-category c610;
-#line 7
-category c611;
-#line 7
-category c612;
-#line 7
-category c613;
-#line 7
-category c614;
-#line 7
-category c615;
-#line 7
-category c616;
-#line 7
-category c617;
-#line 7
-category c618;
-#line 7
-category c619;
-#line 7
-category c620;
-#line 7
-category c621;
-#line 7
-category c622;
-#line 7
-category c623;
-#line 7
-category c624;
-#line 7
-category c625;
-#line 7
-category c626;
-#line 7
-category c627;
-#line 7
-category c628;
-#line 7
-category c629;
-#line 7
-category c630;
-#line 7
-category c631;
-#line 7
-category c632;
-#line 7
-category c633;
-#line 7
-category c634;
-#line 7
-category c635;
-#line 7
-category c636;
-#line 7
-category c637;
-#line 7
-category c638;
-#line 7
-category c639;
-#line 7
-category c640;
-#line 7
-category c641;
-#line 7
-category c642;
-#line 7
-category c643;
-#line 7
-category c644;
-#line 7
-category c645;
-#line 7
-category c646;
-#line 7
-category c647;
-#line 7
-category c648;
-#line 7
-category c649;
-#line 7
-category c650;
-#line 7
-category c651;
-#line 7
-category c652;
-#line 7
-category c653;
-#line 7
-category c654;
-#line 7
-category c655;
-#line 7
-category c656;
-#line 7
-category c657;
-#line 7
-category c658;
-#line 7
-category c659;
-#line 7
-category c660;
-#line 7
-category c661;
-#line 7
-category c662;
-#line 7
-category c663;
-#line 7
-category c664;
-#line 7
-category c665;
-#line 7
-category c666;
-#line 7
-category c667;
-#line 7
-category c668;
-#line 7
-category c669;
-#line 7
-category c670;
-#line 7
-category c671;
-#line 7
-category c672;
-#line 7
-category c673;
-#line 7
-category c674;
-#line 7
-category c675;
-#line 7
-category c676;
-#line 7
-category c677;
-#line 7
-category c678;
-#line 7
-category c679;
-#line 7
-category c680;
-#line 7
-category c681;
-#line 7
-category c682;
-#line 7
-category c683;
-#line 7
-category c684;
-#line 7
-category c685;
-#line 7
-category c686;
-#line 7
-category c687;
-#line 7
-category c688;
-#line 7
-category c689;
-#line 7
-category c690;
-#line 7
-category c691;
-#line 7
-category c692;
-#line 7
-category c693;
-#line 7
-category c694;
-#line 7
-category c695;
-#line 7
-category c696;
-#line 7
-category c697;
-#line 7
-category c698;
-#line 7
-category c699;
-#line 7
-category c700;
-#line 7
-category c701;
-#line 7
-category c702;
-#line 7
-category c703;
-#line 7
-category c704;
-#line 7
-category c705;
-#line 7
-category c706;
-#line 7
-category c707;
-#line 7
-category c708;
-#line 7
-category c709;
-#line 7
-category c710;
-#line 7
-category c711;
-#line 7
-category c712;
-#line 7
-category c713;
-#line 7
-category c714;
-#line 7
-category c715;
-#line 7
-category c716;
-#line 7
-category c717;
-#line 7
-category c718;
-#line 7
-category c719;
-#line 7
-category c720;
-#line 7
-category c721;
-#line 7
-category c722;
-#line 7
-category c723;
-#line 7
-category c724;
-#line 7
-category c725;
-#line 7
-category c726;
-#line 7
-category c727;
-#line 7
-category c728;
-#line 7
-category c729;
-#line 7
-category c730;
-#line 7
-category c731;
-#line 7
-category c732;
-#line 7
-category c733;
-#line 7
-category c734;
-#line 7
-category c735;
-#line 7
-category c736;
-#line 7
-category c737;
-#line 7
-category c738;
-#line 7
-category c739;
-#line 7
-category c740;
-#line 7
-category c741;
-#line 7
-category c742;
-#line 7
-category c743;
-#line 7
-category c744;
-#line 7
-category c745;
-#line 7
-category c746;
-#line 7
-category c747;
-#line 7
-category c748;
-#line 7
-category c749;
-#line 7
-category c750;
-#line 7
-category c751;
-#line 7
-category c752;
-#line 7
-category c753;
-#line 7
-category c754;
-#line 7
-category c755;
-#line 7
-category c756;
-#line 7
-category c757;
-#line 7
-category c758;
-#line 7
-category c759;
-#line 7
-category c760;
-#line 7
-category c761;
-#line 7
-category c762;
-#line 7
-category c763;
-#line 7
-category c764;
-#line 7
-category c765;
-#line 7
-category c766;
-#line 7
-category c767;
-#line 7
-category c768;
-#line 7
-category c769;
-#line 7
-category c770;
-#line 7
-category c771;
-#line 7
-category c772;
-#line 7
-category c773;
-#line 7
-category c774;
-#line 7
-category c775;
-#line 7
-category c776;
-#line 7
-category c777;
-#line 7
-category c778;
-#line 7
-category c779;
-#line 7
-category c780;
-#line 7
-category c781;
-#line 7
-category c782;
-#line 7
-category c783;
-#line 7
-category c784;
-#line 7
-category c785;
-#line 7
-category c786;
-#line 7
-category c787;
-#line 7
-category c788;
-#line 7
-category c789;
-#line 7
-category c790;
-#line 7
-category c791;
-#line 7
-category c792;
-#line 7
-category c793;
-#line 7
-category c794;
-#line 7
-category c795;
-#line 7
-category c796;
-#line 7
-category c797;
-#line 7
-category c798;
-#line 7
-category c799;
-#line 7
-category c800;
-#line 7
-category c801;
-#line 7
-category c802;
-#line 7
-category c803;
-#line 7
-category c804;
-#line 7
-category c805;
-#line 7
-category c806;
-#line 7
-category c807;
-#line 7
-category c808;
-#line 7
-category c809;
-#line 7
-category c810;
-#line 7
-category c811;
-#line 7
-category c812;
-#line 7
-category c813;
-#line 7
-category c814;
-#line 7
-category c815;
-#line 7
-category c816;
-#line 7
-category c817;
-#line 7
-category c818;
-#line 7
-category c819;
-#line 7
-category c820;
-#line 7
-category c821;
-#line 7
-category c822;
-#line 7
-category c823;
-#line 7
-category c824;
-#line 7
-category c825;
-#line 7
-category c826;
-#line 7
-category c827;
-#line 7
-category c828;
-#line 7
-category c829;
-#line 7
-category c830;
-#line 7
-category c831;
-#line 7
-category c832;
-#line 7
-category c833;
-#line 7
-category c834;
-#line 7
-category c835;
-#line 7
-category c836;
-#line 7
-category c837;
-#line 7
-category c838;
-#line 7
-category c839;
-#line 7
-category c840;
-#line 7
-category c841;
-#line 7
-category c842;
-#line 7
-category c843;
-#line 7
-category c844;
-#line 7
-category c845;
-#line 7
-category c846;
-#line 7
-category c847;
-#line 7
-category c848;
-#line 7
-category c849;
-#line 7
-category c850;
-#line 7
-category c851;
-#line 7
-category c852;
-#line 7
-category c853;
-#line 7
-category c854;
-#line 7
-category c855;
-#line 7
-category c856;
-#line 7
-category c857;
-#line 7
-category c858;
-#line 7
-category c859;
-#line 7
-category c860;
-#line 7
-category c861;
-#line 7
-category c862;
-#line 7
-category c863;
-#line 7
-category c864;
-#line 7
-category c865;
-#line 7
-category c866;
-#line 7
-category c867;
-#line 7
-category c868;
-#line 7
-category c869;
-#line 7
-category c870;
-#line 7
-category c871;
-#line 7
-category c872;
-#line 7
-category c873;
-#line 7
-category c874;
-#line 7
-category c875;
-#line 7
-category c876;
-#line 7
-category c877;
-#line 7
-category c878;
-#line 7
-category c879;
-#line 7
-category c880;
-#line 7
-category c881;
-#line 7
-category c882;
-#line 7
-category c883;
-#line 7
-category c884;
-#line 7
-category c885;
-#line 7
-category c886;
-#line 7
-category c887;
-#line 7
-category c888;
-#line 7
-category c889;
-#line 7
-category c890;
-#line 7
-category c891;
-#line 7
-category c892;
-#line 7
-category c893;
-#line 7
-category c894;
-#line 7
-category c895;
-#line 7
-category c896;
-#line 7
-category c897;
-#line 7
-category c898;
-#line 7
-category c899;
-#line 7
-category c900;
-#line 7
-category c901;
-#line 7
-category c902;
-#line 7
-category c903;
-#line 7
-category c904;
-#line 7
-category c905;
-#line 7
-category c906;
-#line 7
-category c907;
-#line 7
-category c908;
-#line 7
-category c909;
-#line 7
-category c910;
-#line 7
-category c911;
-#line 7
-category c912;
-#line 7
-category c913;
-#line 7
-category c914;
-#line 7
-category c915;
-#line 7
-category c916;
-#line 7
-category c917;
-#line 7
-category c918;
-#line 7
-category c919;
-#line 7
-category c920;
-#line 7
-category c921;
-#line 7
-category c922;
-#line 7
-category c923;
-#line 7
-category c924;
-#line 7
-category c925;
-#line 7
-category c926;
-#line 7
-category c927;
-#line 7
-category c928;
-#line 7
-category c929;
-#line 7
-category c930;
-#line 7
-category c931;
-#line 7
-category c932;
-#line 7
-category c933;
-#line 7
-category c934;
-#line 7
-category c935;
-#line 7
-category c936;
-#line 7
-category c937;
-#line 7
-category c938;
-#line 7
-category c939;
-#line 7
-category c940;
-#line 7
-category c941;
-#line 7
-category c942;
-#line 7
-category c943;
-#line 7
-category c944;
-#line 7
-category c945;
-#line 7
-category c946;
-#line 7
-category c947;
-#line 7
-category c948;
-#line 7
-category c949;
-#line 7
-category c950;
-#line 7
-category c951;
-#line 7
-category c952;
-#line 7
-category c953;
-#line 7
-category c954;
-#line 7
-category c955;
-#line 7
-category c956;
-#line 7
-category c957;
-#line 7
-category c958;
-#line 7
-category c959;
-#line 7
-category c960;
-#line 7
-category c961;
-#line 7
-category c962;
-#line 7
-category c963;
-#line 7
-category c964;
-#line 7
-category c965;
-#line 7
-category c966;
-#line 7
-category c967;
-#line 7
-category c968;
-#line 7
-category c969;
-#line 7
-category c970;
-#line 7
-category c971;
-#line 7
-category c972;
-#line 7
-category c973;
-#line 7
-category c974;
-#line 7
-category c975;
-#line 7
-category c976;
-#line 7
-category c977;
-#line 7
-category c978;
-#line 7
-category c979;
-#line 7
-category c980;
-#line 7
-category c981;
-#line 7
-category c982;
-#line 7
-category c983;
-#line 7
-category c984;
-#line 7
-category c985;
-#line 7
-category c986;
-#line 7
-category c987;
-#line 7
-category c988;
-#line 7
-category c989;
-#line 7
-category c990;
-#line 7
-category c991;
-#line 7
-category c992;
-#line 7
-category c993;
-#line 7
-category c994;
-#line 7
-category c995;
-#line 7
-category c996;
-#line 7
-category c997;
-#line 7
-category c998;
-#line 7
-category c999;
-#line 7
-category c1000;
-#line 7
-category c1001;
-#line 7
-category c1002;
-#line 7
-category c1003;
-#line 7
-category c1004;
-#line 7
-category c1005;
-#line 7
-category c1006;
-#line 7
-category c1007;
-#line 7
-category c1008;
-#line 7
-category c1009;
-#line 7
-category c1010;
-#line 7
-category c1011;
-#line 7
-category c1012;
-#line 7
-category c1013;
-#line 7
-category c1014;
-#line 7
-category c1015;
-#line 7
-category c1016;
-#line 7
-category c1017;
-#line 7
-category c1018;
-#line 7
-category c1019;
-#line 7
-category c1020;
-#line 7
-category c1021;
-#line 7
-category c1022;
-#line 7
-category c1023;
-#line 7
-
-
-# Generate level definitions for each sensitivity and category.
-level s0:c0.c1023;
-#line 10
-
-######################################
-# Attribute declarations
-#
-
-# All types used for processes.
-attribute domain;
-
-# Domains that are allowed all permissions ("unconfined").
-attribute unconfineddomain;
-
-# All domains used for apps.
-attribute appdomain;
-
-# All types used for files that can exist on a labeled fs.
-# Do not use for pseudo file types.
-attribute file_type;
-
-# All types used for domain entry points.
-attribute exec_type;
-
-#line 1 "external/sepolicy/bluetooth.te"
-# bluetooth subsystem
-type bluetooth, domain;
-permissive bluetooth;
-
-#line 4
-typeattribute bluetooth appdomain;
-
-#line 5
-typeattribute bluetooth unconfineddomain;
-#line 5
-
-#line 1 "external/sepolicy/healthd.te"
-# healthd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type healthd, domain;
-permissive healthd;
-type healthd_exec, exec_type, file_type;
-
-# New domain is entered by executing the file.
-#line 7
-allow healthd healthd_exec:file { entrypoint read execute };
-
-###
-### Neverallow rules
-###
-### These are things that Android apps should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-
-# Added to make the neverallow rule make sense in a limited environment.
-# Added at the bottom to not throw off file seek numbers in test suite.  
-# This is not a problem, because allow rules are processed after all types
-# are gathered.
-type testTYPE, appdomain, domain;
-
-# added rules for further testing (display full range of needed functionality)
-allow unconfineddomain {fs_type dev_type file_type}:{ chr_file file } ~{entrypoint relabelto};
-
-allow init {fs_type dev_type file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-
-neverallow { appdomain -unconfineddomain } {
-    audio_device
-    camera_device
-    dm_device
-    radio_device
-    gps_device
-    rpmsg_device
-}:chr_file { read write };
\ No newline at end of file
diff --git a/tools/selinux/test/testrunner.py b/tools/selinux/test/testrunner.py
deleted file mode 100755
index bc424e9..0000000
--- a/tools/selinux/test/testrunner.py
+++ /dev/null
@@ -1,442 +0,0 @@
-#!/usr/bin/python
-import sys
-sys.path.append('../src')
-import unittest
-import SELinux_CTS
-from SELinux_CTS import SELinuxPolicy
-
-policy_file_name = 'policy_test.conf'
-types = set([
-        'bluetooth',
-        'healthd',
-        'healthd_exec',
-        'testTYPE' ])  #testTYPE added for neverallow rule to make sense
-attributes = {
-    'domain': set(['bluetooth', 'healthd', 'testTYPE']),
-    'unconfineddomain': set(['bluetooth']),
-    'appdomain': set(['bluetooth', 'testTYPE']),
-    'file_type': set(['healthd_exec']),
-    'exec_type': set(['healthd_exec']) }
-common_classes = {
-    'file': set([
-            'ioctl',
-            'read',
-            'write',
-            'create',
-            'getattr',
-            'setattr',
-            'lock',
-            'relabelfrom',
-            'relabelto',
-            'append',
-            'unlink',
-            'link',
-            'rename',
-            'execute',
-            'swapon',
-            'quotaon',
-            'mounton' ]) }
-classes = {
-    'capability': set([
-            'chown',
-            'dac_override',
-            'dac_read_search',
-            'fowner',
-            'fsetid',
-            'kill',
-            'setgid',
-            'setuid',
-            'setpcap',
-            'linux_immutable',
-            'net_bind_service',
-            'net_broadcast',
-            'net_admin',
-            'net_raw',
-            'ipc_lock',
-            'ipc_owner',
-            'sys_module',
-            'sys_rawio',
-            'sys_chroot',
-            'sys_ptrace',
-            'sys_pacct',
-            'sys_admin',
-            'sys_boot',
-            'sys_nice',
-            'sys_resource',
-            'sys_time',
-            'sys_tty_config',
-            'mknod',
-            'lease',
-            'audit_write',
-            'audit_control',
-            'setfcap' ]),
-    'file': (set([
-                'execute_no_trans',
-                'entrypoint',
-                'execmod',
-                'open',
-                'audit_access' ]) | common_classes['file']) }
-
-# allow healthd healthd_exec:file { entrypoint read execute };
-allow_rules = [
-    { 'source_types': {
-        'set': set([
-                'healthd']),
-        'flags': { 'complement': False } },
-      'target_types': {
-        'set': set([
-                'healthd_exec']),
-        'flags': { 'complement': False } },
-      'classes': {
-        'set': set([
-                'file']),
-        'flags': { 'complement': False } },
-      'permissions': {
-        'set': set([
-                'entrypoint',
-                'read',
-                'execute' ]),
-        'flags': { 'complement': False } } } ]
-
-# neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-neverallow_rules = [
-    { 'source_types': {
-        'set': set([
-                'appdomain',
-                '-unconfineddomain',
-                '-bluetooth' ]),
-        'flags': { 'complement': False } },
-      'target_types': {
-        'set': set([
-                'self']),
-        'flags': { 'complement': False } },
-      'classes': {
-        'set': set([
-                'capability']),
-        'flags': { 'complement': False } },
-      'permissions': {
-        'set': set([
-                '*' ]),
-        'flags': { 'complement': False } } } ]
-
-expected_final_allow_list = [
-        [ ('healthd', 'healthd_exec', 'file', 'entrypoint'),
-                ('healthd', 'healthd_exec', 'file', 'read'),
-                ('healthd', 'healthd_exec', 'file', 'execute') ] ]
-
-expected_final_neverallow_list = [
-        [ ('testTYPE', 'testTYPE', 'capability', 'chown'),
-                ('testTYPE', 'testTYPE', 'capability', 'dac_override'),
-                ('testTYPE', 'testTYPE', 'capability', 'dac_read_search'),
-                ('testTYPE', 'testTYPE', 'capability', 'fowner'),
-                ('testTYPE', 'testTYPE', 'capability', 'fsetid'),
-                ('testTYPE', 'testTYPE', 'capability', 'kill'),
-                ('testTYPE', 'testTYPE', 'capability', 'setgid'),
-                ('testTYPE', 'testTYPE', 'capability', 'setuid'),
-                ('testTYPE', 'testTYPE', 'capability', 'setpcap'),
-                ('testTYPE', 'testTYPE', 'capability', 'linux_immutable'),
-                ('testTYPE', 'testTYPE', 'capability', 'net_bind_service'),
-                ('testTYPE', 'testTYPE', 'capability', 'net_broadcast'),
-                ('testTYPE', 'testTYPE', 'capability', 'net_admin'),
-                ('testTYPE', 'testTYPE', 'capability', 'net_raw'),
-                ('testTYPE', 'testTYPE', 'capability', 'ipc_lock'),
-                ('testTYPE', 'testTYPE', 'capability', 'ipc_owner'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_module'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_rawio'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_chroot'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_ptrace'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_pacct'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_admin'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_boot'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_nice'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_resource'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_time'),
-                ('testTYPE', 'testTYPE', 'capability', 'sys_tty_config'),
-                ('testTYPE', 'testTYPE', 'capability', 'mknod'),
-                ('testTYPE', 'testTYPE', 'capability', 'lease'),
-                ('testTYPE', 'testTYPE', 'capability', 'audit_write'),
-                ('testTYPE', 'testTYPE', 'capability', 'audit_control'),
-                ('testTYPE', 'testTYPE', 'capability', 'setfcap') ] ]
-
-
-class SELinuxPolicyTests(unittest.TestCase):
-
-
-    def setUp(self):
-        self.test_policy = SELinuxPolicy()
-        self.test_file = open(policy_file_name, 'r')
-        self.test_policy.types = types
-        self.test_policy.attributes = attributes
-        self.test_policy.common_classes = common_classes
-        self.test_policy.classes = classes
-        self.test_policy.allow_rules = allow_rules
-        self.test_policy.neverallow_rules = neverallow_rules
-        return
-
-    def testExpandAvcRule(self):
-        #TODO: add more examples here to cover different cases
-        expanded_allow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.allow_rules[0])
-        for a in expected_final_allow_list[0]:
-            self.failUnless(a in expanded_allow_list)
-        expanded_neverallow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.neverallow_rules[0])
-        for n in expected_final_neverallow_list[0]:
-            self.failUnless(n in expanded_neverallow_list)
-
-    def testExpandBrackets(self):
-        #test position without bracket:
-        self.test_file.seek(279)
-        self.failIf(SELinux_CTS.expand_brackets(self.test_file))
-
-        #test position with bracket:
-        self.test_file.seek(26123)
-        self.failUnless(SELinux_CTS.expand_brackets(self.test_file) == " entrypoint read execute ")
-
-        #test position with nested brackets:
-        self.test_file.seek(26873)
-        self.failUnless(SELinux_CTS.expand_brackets(self.test_file)
-               == " dir   chr_file blk_file   file lnk_file sock_file fifo_file   ")
-
-    def testGetAvcRuleComponent(self):
-        #test against normal ('allow healthd healthd_exec:file ...)
-        self.test_file.seek(26096)
-        normal_src = { 'flags': { 'complement': False },
-                'set': set(['healthd']) }
-        normal_tgt = { 'flags': { 'complement': False },
-                'set': set(['healthd_exec']) }
-        normal_class = { 'flags': { 'complement': False },
-                'set': set(['file']) }
-        normal_perm = { 'flags': { 'complement': False },
-                'set': set(['entrypoint', 'read', 'execute']) }
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == normal_src)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == normal_tgt)
-        c = SELinux_CTS.advance_past_whitespace(self.test_file)
-        if c == ':':
-            self.test_file.read(1)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == normal_class)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == normal_perm)
-
-        #test against 'hard' ('init {fs_type  ...' )
-        self.test_file.seek(26838)
-        hard_src = { 'flags': { 'complement': False },
-                'set': set(['init']) }
-        hard_tgt = { 'flags': { 'complement': False },
-                'set': set(['fs_type', 'dev_type', 'file_type']) }
-        hard_class = { 'flags': { 'complement': False },
-                'set': set(['dir', 'chr_file', 'blk_file', 'file', 'lnk_file', 'sock_file', 'fifo_file']) }
-        hard_perm = { 'flags': { 'complement': False },
-                'set': set(['relabelto']) }
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == hard_src)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == hard_tgt)
-        #mimic ':' check:
-        c = SELinux_CTS.advance_past_whitespace(self.test_file)
-        if c == ':':
-            self.test_file.read(1)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == hard_class)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == hard_perm)
-
-        #test against 'multi-line' ('init {fs_type  ...' )
-        self.test_file.seek(26967)
-        multi_src = { 'flags': { 'complement': False },
-                'set': set(['appdomain', '-unconfineddomain']) }
-        multi_tgt = { 'flags': { 'complement': False },
-                'set': set(['audio_device', 'camera_device', 'dm_device', 'radio_device', 'gps_device', 'rpmsg_device']) }
-        multi_class = { 'flags': { 'complement': False },
-                'set': set(['chr_file']) }
-        multi_perm = { 'flags': { 'complement': False },
-                'set': set(['read', 'write']) }
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == multi_src)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == multi_tgt)
-        c = SELinux_CTS.advance_past_whitespace(self.test_file)
-        if c == ':':
-            self.test_file.read(1)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == multi_class)
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == multi_perm)
-
-        #test against 'complement'
-        self.test_file.seek(26806)
-        complement = { 'flags': { 'complement': True },
-                'set': set(['entrypoint', 'relabelto']) }
-        self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
-            == complement)
-
-    def testGetLineType(self):
-        self.failUnless(SELinux_CTS.get_line_type('type bluetooth, domain;')
-                == SELinux_CTS.TYPE)
-        self.failUnless(SELinux_CTS.get_line_type('attribute unconfineddomain;')
-                == SELinux_CTS.ATTRIBUTE)
-        self.failUnless(SELinux_CTS.get_line_type('typeattribute bluetooth appdomain;')
-                == SELinux_CTS.TYPEATTRIBUTE)
-        self.failUnless(SELinux_CTS.get_line_type('class file')
-                == SELinux_CTS.CLASS)
-        self.failUnless(SELinux_CTS.get_line_type('common file')
-                == SELinux_CTS.COMMON)
-        self.failUnless(SELinux_CTS.get_line_type('allow healthd healthd_exec:file { entrypoint read execute };')
-                == SELinux_CTS.ALLOW_RULE)
-        self.failUnless(SELinux_CTS.get_line_type('neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;')
-                == SELinux_CTS.NEVERALLOW_RULE)
-        self.failUnless(SELinux_CTS.get_line_type('# FLASK')
-                == SELinux_CTS.OTHER)
-
-    def testIsMultiLine(self):
-        self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPE))
-        self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.ATTRIBUTE))
-        self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPEATTRIBUTE))
-        self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.CLASS))
-        self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.COMMON))
-        self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.ALLOW_RULE))
-        self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.NEVERALLOW_RULE))
-        self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.OTHER))
-
-    def testProcessInheritsSegment(self):
-        inherit_offset = 448 # needs changing if file changes
-        self.test_file.seek(inherit_offset, 0)
-        inherit_result = SELinux_CTS.process_inherits_segment(self.test_file)
-        self.failUnless(inherit_result == 'file')
-        return
-
-    def testFromFileName(self):
-        #using a special file, since the test_file has some lines which don't 'jive'
-        clean_policy_file = 'policy_clean_test.conf'
-        from_file_policy = SELinuxPolicy()
-        from_file_policy.from_file_name(clean_policy_file)
-        self.failUnless(from_file_policy.types == self.test_policy.types)
-        self.failUnless(from_file_policy.attributes == self.test_policy.attributes)
-        self.failUnless(from_file_policy.classes == self.test_policy.classes)
-        self.failUnless(from_file_policy.common_classes == self.test_policy.common_classes)
-        self.failUnless(from_file_policy.allow_rules == self.test_policy.allow_rules)
-        self.failUnless(from_file_policy.neverallow_rules == self.test_policy.neverallow_rules)
-
-    def testExpandPermissions(self):
-        #test general case
-        test_class_obj = 'file'
-        general_set = set(['read', 'write', 'execute'])
-        expanded_general_set = general_set
-        self.failUnless(self.test_policy.expand_permissions(test_class_obj, general_set)
-                == general_set)
-        star_set = set(['*'])
-        expanded_star_set = self.test_policy.classes['file'] #everything in the class
-        self.failUnless(self.test_policy.expand_permissions(test_class_obj, star_set)
-                == expanded_star_set)
-        complement_set = set(['*', '-open'])
-        expanded_complement_set = self.test_policy.classes['file'] - set(['open'])
-        self.failUnless(self.test_policy.expand_permissions(test_class_obj, complement_set)
-                == expanded_complement_set)
-
-    def testExpandTypes(self):
-
-        #test general case and '-' handling
-        test_source_set = set([
-                'domain',
-                '-bluetooth' ])
-        expanded_test_source_set = set([
-                'healthd', 'testTYPE' ])
-        self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set)
-
-        #test '*' handling
-        test_source_set = set([ '*' ])
-        expanded_test_source_set = set([
-                'bluetooth', 'healthd', 'testTYPE' ])
-        self.failUnless(self.test_policy.expand_types(test_source_set) == types)
-        #test - handling
-        test_source_set = set([
-                '*',
-                '-bluetooth'])
-        expanded_test_source_set = set([
-                'healthd', 'healthd_exec', 'testTYPE' ])
-        self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set)
-
-    def testProcessAttributeLine(self):
-        attribute_policy = SELinuxPolicy()
-        #test with 'normal input'
-        test_normal_string = 'attribute TEST_att;'
-        test_attribute = 'TEST_att'
-        attribute_policy.process_attribute_line(test_normal_string)
-        self.failUnless( test_attribute in attribute_policy.attributes)
-        #TODO: test on bogus inputs
-
-    def testProcessClassLine(self):
-        class_policy = SELinuxPolicy()
-        #offsets need changing if test file changes
-        common_offset  = 279
-        class_initial_offset  = 212
-        class_perm_offset = 437
-        self.test_file.seek(common_offset, 0)
-        line = self.test_file.readline()
-        class_policy.process_common_line(line, self.test_file)
-        self.test_file.seek(class_initial_offset, 0)
-        line = self.test_file.readline()
-        class_policy.process_class_line(line, self.test_file)
-        self.failUnless('file' in class_policy.classes)
-        self.test_file.seek(class_perm_offset, 0)
-        line = self.test_file.readline()
-        class_policy.process_class_line(line, self.test_file)
-        self.failUnless(class_policy.classes['file'] == classes['file'])
-
-    def testProcessCommonLine(self):
-        common_policy = SELinuxPolicy()
-        common_offset  = 279 # needs changing if file changes
-        self.test_file.seek(common_offset, 0)
-        line = self.test_file.readline()
-        common_policy.process_common_line(line, self.test_file)
-        self.failUnless('file' in common_policy.common_classes )
-        self.failUnless(common_policy.common_classes['file'] == common_classes['file'])
-
-    def testProcessAvcRuleLine(self):
-        avc_policy = SELinuxPolicy()
-        allow_offset  =  26091 # needs changing if file changes
-        neverallow_offset  = 26311  # needs changing if file changes
-        self.test_file.seek(allow_offset, 0)
-        line = self.test_file.readline()
-        avc_policy.process_avc_rule_line(line, self.test_file)
-        self.failUnless(avc_policy.allow_rules[0] == allow_rules[0] ) # always '0'?
-        self.test_file.seek(neverallow_offset, 0)
-        line = self.test_file.readline()
-        avc_policy.process_avc_rule_line(line, self.test_file)
-        self.failUnless(avc_policy.neverallow_rules[0] == neverallow_rules[0] ) # always '0'?
-
-    def testProcessTypeLine(self):
-        type_policy = SELinuxPolicy()
-        test_normal_string = 'type TEST_type, TEST_att1, TEST_att2;'
-        test_type = 'TEST_type'
-        test_atts = ['TEST_att1', 'TEST_att2']
-        #test with 'normal input'
-        type_policy.process_type_line(test_normal_string)
-        self.failUnless(test_type in type_policy.types)
-        for a in test_atts:
-            self.failUnless(a in type_policy.attributes)
-            self.failUnless(test_type in type_policy.attributes[a])
-        #TODO: test with domain only, no attributes
-        # and test on bogus inputs
-
-    def testProcessTypeattributeLine(self):
-        typ_att_policy = SELinuxPolicy()
-        test_normal_string = 'typeattribute TEST_type TEST_att1, TEST_att2;'
-        test_type = 'TEST_type'
-        test_atts = ['TEST_att1', 'TEST_att2']
-        #test with 'normal input' (type should already be declared)
-        typ_att_policy.process_type_line('type ' + test_type + ';')
-        typ_att_policy.process_typeattribute_line(test_normal_string)
-        self.failUnless(test_type in typ_att_policy.types)
-        for a in test_atts:
-            self.failUnless(a in typ_att_policy.attributes)
-            self.failUnless(test_type in typ_att_policy.attributes[a])
-        #TODO: test with domain only, no attributes
-        # and test on bogus inputs
-
-def main():
-    unittest.main()
-
-if __name__ == '__main__':
-    main()
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index c43183a..16860d5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -31,7 +31,7 @@
     @Option(name="cts-install-path", description="the path to the cts installation to use")
     private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
 
-    public static final String CTS_BUILD_VERSION = "5.0_r2.5";
+    public static final String CTS_BUILD_VERSION = "5.1_r1";
 
     /**
      * {@inheritDoc}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java
index 0c947c3..96145e9 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java
@@ -95,6 +95,8 @@
                 getMetric(metricsCopy, DeviceInfoConstants.SCREEN_DENSITY_BUCKET));
         serializer.attribute(ns, DeviceInfoConstants.SCREEN_SIZE,
                 getMetric(metricsCopy, DeviceInfoConstants.SCREEN_SIZE));
+        serializer.attribute(ns, DeviceInfoConstants.SMALLEST_SCREEN_WIDTH_DP,
+                getMetric(metricsCopy, DeviceInfoConstants.SMALLEST_SCREEN_WIDTH_DP));
         serializer.endTag(ns, SCREEN_TAG);
 
         serializer.startTag(ns, PHONE_TAG);
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index ce48664..c2c2a10 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -707,9 +707,9 @@
             }
             mTestPackageList.addAll(shardTestPackageList);
         } catch (FileNotFoundException e) {
-            throw new IllegalArgumentException("failed to find XTS plan file", e);
+            throw new IllegalArgumentException("failed to find test plan file", e);
         } catch (ParseException e) {
-            throw new IllegalArgumentException("failed to parse XTS plan file", e);
+            throw new IllegalArgumentException("failed to parse test plan file", e);
         } catch (ConfigurationException e) {
             throw new IllegalArgumentException("failed to process arguments", e);
         }
@@ -747,7 +747,7 @@
                 testPkgDefs.add(testPackageDef);
             }
         } else if (mPackageNames.size() > 0){
-            Log.i(LOG_TAG, String.format("Executing XTS test packages %s", mPackageNames));
+            Log.i(LOG_TAG, String.format("Executing test packages %s", mPackageNames));
 
             Map<String, List<ITestPackageDef>> testPackageDefMap =
                     testRepo.getTestPackageDefsByName();
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
index 59bfcf8..3dee99e 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
@@ -29,7 +29,7 @@
     private static final String OVERLAY_DISPLAY_DEVICES_SETTING_NAME = "overlay_display_devices";
 
     // Use a non-standard pattern, must match values in tests/tests/display/.../DisplayTest.java
-    private static final String OVERLAY_DISPLAY_DEVICES_SETTING_VALUE = "1281x721/214";
+    private static final String OVERLAY_DISPLAY_DEVICES_SETTING_VALUE = "181x161/214";
 
     @Override
     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
index 3d92eb3..72dccd4 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
@@ -185,6 +185,11 @@
     }
 
     @Override
+    public void setTestCollection(boolean b) {
+        throw new UnsupportedOperationException("Test Collection mode is not supported");
+    }
+
+    @Override
     public void setTestSize(TestSize size) {
         addInstrumentationArg(SIZE_ARG_NAME, ""/*size.getRunnerValue()*/);
     }
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index ed74824..95b77f2 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -78,16 +78,26 @@
         final String outputPathPrefix = args[0];
         File manifestFile = new File(args[1]);
         String jarFileName = args[2];
-        final String javaPackageFilter = args[3];
-        // Validate the javaPackageFilter value if non null.
-        if (javaPackageFilter.length() != 0) {
-            if (!isValidJavaPackage(javaPackageFilter)) {
-                System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " +
-                        javaPackageFilter);
-                System.exit(1);
-                return;
+        final String javaPackageFilterArg = args[3] != null ? args[3].replaceAll("\\s+", "") : "";
+        final String[] javaPackagePrefixes;
+        // Validate the javaPackageFilter value if non-empty.
+        if (!javaPackageFilterArg.isEmpty()) {
+            javaPackagePrefixes = javaPackageFilterArg.split(":");
+            for (int i = 0; i < javaPackagePrefixes.length; ++i) {
+                final String javaPackageFilter = javaPackagePrefixes[i];
+                if (!isValidJavaPackage(javaPackageFilter)) {
+                    System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " +
+                           javaPackageFilter);
+                    System.exit(1);
+                    return;
+                } else {
+                    javaPackagePrefixes[i] = javaPackageFilter.trim() + ".";
+                }
             }
+        } else {
+            javaPackagePrefixes = new String[0];
         }
+
         String architecture = args[4];
         if (architecture == null || architecture.equals("")) {
             System.err.println("Invalid architecture");
@@ -132,7 +142,7 @@
                     setAttribute(testPackageElem, ATTRIBUTE_RUNNER, runner);
                     setAttribute(testPackageElem, ATTRIBUTE_PACKAGE, packageName);
                     setAttribute(testPackageElem, ATTRIBUTE_NS, packageName);
-                    setAttribute(testPackageElem, ATTRIBUTE_JAVA_PACKAGE_FILTER, javaPackageFilter);
+                    setAttribute(testPackageElem, ATTRIBUTE_JAVA_PACKAGE_FILTER, javaPackageFilterArg);
 
                     if (testType.type == TestType.HOST_SIDE_ONLY) {
                         setAttribute(testPackageElem, ATTRIBUTE_HOST_SIDE_ONLY, "true");
@@ -185,8 +195,6 @@
 
         Map<String,TestClass> testCases = new LinkedHashMap<String, TestClass>();
 
-        String javaPackagePrefix = javaPackageFilter.isEmpty() ? "" : (javaPackageFilter + ".");
-
         Enumeration<JarEntry> jarEntries = jarFile.entries();
         while (jarEntries.hasMoreElements()) {
             JarEntry jarEntry = jarEntries.nextElement();
@@ -196,9 +204,22 @@
             }
             String className
                     = name.substring(0, name.length() - ".class".length()).replace('/', '.');
-            if (!className.startsWith(javaPackagePrefix)) {
+
+            boolean matchesPrefix = false;
+            if (javaPackagePrefixes.length > 0) {
+                for (String javaPackagePrefix : javaPackagePrefixes) {
+                    if (className.startsWith(javaPackagePrefix)) {
+                        matchesPrefix = true;
+                    }
+                }
+            } else {
+                matchesPrefix = true;
+            }
+
+            if (!matchesPrefix) {
                 continue;
             }
+
             try {
                 Class<?> klass = Class.forName(className,
                                                false,