Merge tag 'android-6.0.0_r26' into r1

Android 6.0.0 release 26

Force reset to android-6.0.0_r26 to eliminate deviations:
renamed:    tests/tests/netlegacy22/api/Android.mk -> tests/netlegacy22.api/Android.mk
deleted:    tests/tests/netlegacy22/permission/Android.mk
deleted:    tests/tests/netlegacy22/permission/AndroidManifest.xml
deleted:    tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
deleted:    tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java

* tag 'android-6.0.0_r26': (239 commits)
  Camera: improve Image allocation in camera CTS
  DO NOT MERGE: The new AudioPolicyBinder tests are not yet robust enough to be added to the test suite, hence adding them to knownfailures
  Refactored "Audio Devices Notifications" and "Audio Routing Notifications" testing into separate Input & Output flavors.
  disabled protected broadcast
  Fix android.print test to skip when feature is missing
  Revert "Separating phone and telecom specific protected broadcasts.  Bug: 23192492"
  Separating phone and telecom specific protected broadcasts.  Bug: 23192492
  Separating phone and telecom specific protected broadcasts. Bug: 23192492
  DO NOT MERGE CTS version is now 6.0_r1
  Separating phone and telecom specific protected broadcasts. Bug: 23192492
  added back adopthost test removed video encoding test.
  Add whitelists for known address, port, and UID combinations Bug: 19461976
  Save expected audio route for speaker tests, but disable for now
  bug: 21208382
  [cts] fix build break
  BUG: 21262226,23979591,23040468,23144425
  Fix android.text.format.cts.DateUtilsTest#test_getRelativeTimeSpanString
  cts: Check for video codec on some MediaRecorder Tests
  Add density requirements for watches
  DO NOT MERGE: Revert "Revert "Fix for ByodFlowTestActivity""
  ...

Change-Id: I781414a4314a2d42fcc4530243098a117552a25d
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 41f9dc3..975ac47 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -194,6 +194,7 @@
     CtsSecurityTestCases \
     CtsSignatureTestCases \
     CtsSpeechTestCases \
+    CtsSystemUiTestCases \
     CtsTelecomTestCases \
     CtsTelecomTestCases2 \
     CtsTelephonyTestCases \
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 756f959..835a4a4 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -448,8 +448,8 @@
 
         The out_surfaces field can specify the width(s), height(s), and
         format(s) of the captured image. The formats may be "yuv", "jpeg",
-        "dng", "raw", "raw10", or "raw12". The default is a YUV420 frame ("yuv")
-        corresponding to a full sensor frame.
+        "dng", "raw", "raw10", "raw12", or "rawStats". The default is a YUV420
+        frame ("yuv") corresponding to a full sensor frame.
 
         Note that one or more surfaces can be specified, allowing a capture to
         request images back in multiple formats (e.g.) raw+yuv, raw+jpeg,
@@ -536,6 +536,25 @@
             yuv_caps           = do_capture( [req1,req2], yuv_fmt           )
             yuv_caps, raw_caps = do_capture( [req1,req2], [yuv_fmt,raw_fmt] )
 
+        The "rawStats" format processes the raw image and returns a new image
+        of statistics from the raw image. The format takes additional keys,
+        "gridWidth" and "gridHeight" which are size of grid cells in a 2D grid
+        of the raw image. For each grid cell, the mean and variance of each raw
+        channel is computed, and the do_capture call returns two 4-element float
+        images of dimensions (rawWidth / gridWidth, rawHeight / gridHeight),
+        concatenated back-to-back, where the first iamge contains the 4-channel
+        means and the second contains the 4-channel variances.
+
+        For the rawStats format, if the gridWidth is not provided then the raw
+        image width is used as the default, and similarly for gridHeight. With
+        this, the following is an example of a output description that computes
+        the mean and variance across each image row:
+
+            {
+                "gridHeight": 1,
+                "format": "rawStats"
+            }
+
         Args:
             cap_request: The Python dict/list specifying the capture(s), which
                 will be converted to JSON and sent to the device.
@@ -550,7 +569,8 @@
             * data: the image data as a numpy array of bytes.
             * width: the width of the captured image.
             * height: the height of the captured image.
-            * format: image the format, in ["yuv","jpeg","raw","raw10","dng"].
+            * format: image the format, in [
+                        "yuv","jpeg","raw","raw10","raw12","rawStats","dng"].
             * metadata: the capture result object (Python dictionary).
         """
         cmd = {}
@@ -577,9 +597,13 @@
         nsurf = 1 if out_surfaces is None else len(cmd["outputSurfaces"])
         if len(formats) > len(set(formats)):
             raise its.error.Error('Duplicate format requested')
-        if "dng" in formats and "raw" in formats or \
-                "dng" in formats and "raw10" in formats or \
-                "raw" in formats and "raw10" in formats:
+        raw_formats = 0;
+        raw_formats += 1 if "dng" in formats else 0
+        raw_formats += 1 if "raw" in formats else 0
+        raw_formats += 1 if "raw10" in formats else 0
+        raw_formats += 1 if "raw12" in formats else 0
+        raw_formats += 1 if "rawStats" in formats else 0
+        if raw_formats > 1:
             raise its.error.Error('Different raw formats not supported')
 
         # Detect long exposure time and set timeout accordingly
@@ -603,14 +627,16 @@
         # the burst, however individual images of different formats can come
         # out in any order for that capture.
         nbufs = 0
-        bufs = {"yuv":[], "raw":[], "raw10":[], "dng":[], "jpeg":[]}
+        bufs = {"yuv":[], "raw":[], "raw10":[], "raw12":[],
+                "rawStats":[], "dng":[], "jpeg":[]}
         mds = []
         widths = None
         heights = None
         while nbufs < ncap*nsurf or len(mds) < ncap:
             jsonObj,buf = self.__read_response_from_socket()
             if jsonObj['tag'] in ['jpegImage', 'yuvImage', 'rawImage', \
-                    'raw10Image', 'dngImage'] and buf is not None:
+                    'raw10Image', 'raw12Image', 'rawStatsImage', 'dngImage'] \
+                    and buf is not None:
                 fmt = jsonObj['tag'][:-5]
                 bufs[fmt].append(buf)
                 nbufs += 1
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index ea01a3e..a5ac60b 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -81,6 +81,25 @@
     else:
         raise its.error.Error('Invalid format %s' % (cap["format"]))
 
+def unpack_rawstats_capture(cap):
+    """Unpack a rawStats capture to the mean and variance images.
+
+    Args:
+        cap: A capture object as returned by its.device.do_capture.
+
+    Returns:
+        Tuple (mean_image var_image) of float-4 images, with non-normalized
+        pixel values computed from the RAW16 images on the device
+    """
+    assert(cap["format"] == "rawStats")
+    w = cap["width"]
+    h = cap["height"]
+    img = numpy.ndarray(shape=(2*h*w*4,), dtype='<f', buffer=cap["data"])
+    analysis_image = img.reshape(2,h,w,4)
+    mean_image = analysis_image[0,:,:,:].reshape(h,w,4)
+    var_image = analysis_image[1,:,:,:].reshape(h,w,4)
+    return mean_image, var_image
+
 def unpack_raw10_capture(cap, props):
     """Unpack a raw-10 capture to a raw-16 capture.
 
@@ -604,6 +623,21 @@
         variances.append(numpy.var(img[:,:,i], dtype=numpy.float64))
     return variances
 
+def compute_image_snrs(img):
+    """Calculate the SNR (db) of each color channel in the image.
+
+    Args:
+        img: Numpy float image array, with pixel values in [0,1].
+
+    Returns:
+        A list of SNR value, one per color channel in the image.
+    """
+    means = compute_image_means(img)
+    variances = compute_image_variances(img)
+    std_devs = [math.sqrt(v) for v in variances]
+    snr = [20 * math.log10(m/s) for m,s in zip(means, std_devs)]
+    return snr
+
 def write_image(img, fname, apply_gamma=False):
     """Save a float-3 numpy array image to a file.
 
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index 82346ec..ac384fb 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -152,24 +152,37 @@
 
     return req
 
-def get_available_output_sizes(fmt, props):
+def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
     """Return a sorted list of available output sizes for a given format.
 
     Args:
         fmt: the output format, as a string in
             ["jpg", "yuv", "raw", "raw10", "raw12"].
         props: the object returned from its.device.get_camera_properties().
+        max_size: (Optional) A (w,h) tuple.
+            Sizes larger than max_size (either w or h)  will be discarded.
+        match_ar_size: (Optional) A (w,h) tuple.
+            Sizes not matching the aspect ratio of match_ar_size will be
+            discarded.
 
     Returns:
         A sorted list of (w,h) tuples (sorted large-to-small).
     """
-    fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26, "yuv":0x23,
+    AR_TOLERANCE = 0.03
+    fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
                  "jpg":0x100, "jpeg":0x100}
     configs = props['android.scaler.streamConfigurationMap']\
                    ['availableStreamConfigurations']
     fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
     out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
     out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
+    if max_size:
+        out_sizes = [s for s in out_sizes if
+                s[0] <= max_size[0] and s[1] <= max_size[1]]
+    if match_ar_size:
+        ar = match_ar_size[0] / float(match_ar_size[1])
+        out_sizes = [s for s in out_sizes if
+                abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
     out_sizes.sort(reverse=True)
     return out_sizes
 
diff --git a/apps/CameraITS/tests/inprog/test_rawstats.py b/apps/CameraITS/tests/inprog/test_rawstats.py
new file mode 100644
index 0000000..8083f0b
--- /dev/null
+++ b/apps/CameraITS/tests/inprog/test_rawstats.py
@@ -0,0 +1,48 @@
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import its.image
+import its.caps
+import its.device
+import its.objects
+import its.target
+import os.path
+import math
+
+def main():
+    """Test capturing some rawstats data.
+    """
+    NAME = os.path.basename(__file__).split(".")[0]
+
+    with its.device.ItsSession() as cam:
+
+        cam.do_3a(do_af=False);
+        req = its.objects.auto_capture_request()
+
+        for (gw,gh) in [(16,16)]:#,(4080,1)]:
+            cap = cam.do_capture(req,
+                {"format":"rawStats","gridWidth":gw,"gridHeight":gh})
+            mean_image, var_image = its.image.unpack_rawstats_capture(cap)
+
+            if gw > 1 and gh > 1:
+                h,w,_ = mean_image.shape
+                for ch in range(4):
+                    m = mean_image[:,:,ch].reshape(h,w,1)/1023.0
+                    v = var_image[:,:,ch].reshape(h,w,1)
+                    its.image.write_image(m, "%s_mean_ch%d.jpg" % (NAME,ch), True)
+                    its.image.write_image(v, "%s_var_ch%d.jpg" % (NAME,ch), True)
+
+if __name__ == '__main__':
+    main()
+
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1/test_exposure.py
index 26c398d..89bc724 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1/test_exposure.py
@@ -35,8 +35,10 @@
     THRESHOLD_MAX_OUTLIER_DIFF = 0.1
     THRESHOLD_MIN_LEVEL = 0.1
     THRESHOLD_MAX_LEVEL = 0.9
-    THRESHOLD_MAX_LEVEL_DIFF = 0.025
+    THRESHOLD_MAX_LEVEL_DIFF = 0.03
     THRESHOLD_MAX_LEVEL_DIFF_WIDE_RANGE = 0.05
+    THRESHOLD_ROUND_DOWN_GAIN = 0.1
+    THRESHOLD_ROUND_DOWN_EXP = 0.05
 
     mults = []
     r_means = []
@@ -50,21 +52,33 @@
                              its.caps.per_frame_control(props))
 
         e,s = its.target.get_target_exposure_combos(cam)["minSensitivity"]
+        s_e_product = s*e
         expt_range = props['android.sensor.info.exposureTimeRange']
         sens_range = props['android.sensor.info.sensitivityRange']
 
-        m = 1
+        m = 1.0
         while s*m < sens_range[1] and e/m > expt_range[0]:
             mults.append(m)
-            req = its.objects.manual_capture_request(s*m, e/m)
+            s_test = round(s*m)
+            e_test = s_e_product / s_test
+            print "Testsing s:", s_test, "e:", e_test
+            req = its.objects.manual_capture_request(s_test, e_test)
             cap = cam.do_capture(req)
+            s_res = cap["metadata"]["android.sensor.sensitivity"]
+            e_res = cap["metadata"]["android.sensor.exposureTime"]
+            assert(0 <= s_test - s_res < s_test * THRESHOLD_ROUND_DOWN_GAIN)
+            assert(0 <= e_test - e_res < e_test * THRESHOLD_ROUND_DOWN_EXP)
+            s_e_product_res = s_res * e_res
+            request_result_ratio = s_e_product / s_e_product_res
+            print "Capture result s:", s_test, "e:", e_test
             img = its.image.convert_capture_to_rgb_image(cap)
             its.image.write_image(img, "%s_mult=%3.2f.jpg" % (NAME, m))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
             rgb_means = its.image.compute_image_means(tile)
-            r_means.append(rgb_means[0])
-            g_means.append(rgb_means[1])
-            b_means.append(rgb_means[2])
+            # Adjust for the difference between request and result
+            r_means.append(rgb_means[0] * request_result_ratio)
+            g_means.append(rgb_means[1] * request_result_ratio)
+            b_means.append(rgb_means[2] * request_result_ratio)
             # Test 3 steps per 2x gain
             m = m * pow(2, 1.0 / 3)
 
@@ -73,9 +87,9 @@
             threshold_max_level_diff = THRESHOLD_MAX_LEVEL_DIFF_WIDE_RANGE
 
     # Draw a plot.
-    pylab.plot(mults, r_means, 'r')
-    pylab.plot(mults, g_means, 'g')
-    pylab.plot(mults, b_means, 'b')
+    pylab.plot(mults, r_means, 'r.-')
+    pylab.plot(mults, g_means, 'g.-')
+    pylab.plot(mults, b_means, 'b.-')
     pylab.ylim([0,1])
     matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index 2c8d73b..1072684 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -35,13 +35,13 @@
     """
     NAME = os.path.basename(__file__).split(".")[0]
 
-    RELATIVE_ERROR_TOLERANCE = 0.1
+    NUM_SAMPLES_PER_MODE = 4
+    SNR_TOLERANCE = 3 # unit in db
+    # List of SNRs for R,G,B.
+    snrs = [[], [], []]
 
-    # List of variances for Y,U,V.
-    variances = [[],[],[]]
-
-    # Reference (baseline) variance for each of Y,U,V.
-    ref_variance = []
+    # Reference (baseline) SNR for each of R,G,B.
+    ref_snr = []
 
     nr_modes_reported = []
 
@@ -56,74 +56,89 @@
         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])
-        print "Ref variances:", ref_variance
+        rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
+        ref_snr = its.image.compute_image_snrs(rgb_tile)
+        print "Ref SNRs:", ref_snr
 
+        e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
         # NR modes 0, 1, 2, 3, 4 with high gain
         for mode in range(5):
             # Skip unavailable modes
             if not its.caps.noise_reduction_mode(props, mode):
                 nr_modes_reported.append(mode)
                 for channel in range(3):
-                    variances[channel].append(0)
+                    snrs[channel].append(0)
                 continue;
 
-            e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
-            req = its.objects.manual_capture_request(s, e)
-            req["android.noiseReduction.mode"] = mode
-            cap = cam.do_capture(req)
-            nr_modes_reported.append(
-                    cap["metadata"]["android.noiseReduction.mode"])
-            its.image.write_image(
-                    its.image.convert_capture_to_rgb_image(cap),
-                    "%s_high_gain_nr=%d.jpg" % (NAME, mode))
-            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])
-        print "Variances with NR mode [0,1,2,3,4]:", variances
+            rgb_snr_list = []
+            # Capture several images to account for per frame noise variations
+            for n in range(NUM_SAMPLES_PER_MODE):
+                req = its.objects.manual_capture_request(s, e)
+                req["android.noiseReduction.mode"] = mode
+                cap = cam.do_capture(req)
+                rgb_image = its.image.convert_capture_to_rgb_image(cap)
+                if n == 0:
+                    nr_modes_reported.append(
+                            cap["metadata"]["android.noiseReduction.mode"])
+                    its.image.write_image(
+                            rgb_image,
+                            "%s_high_gain_nr=%d.jpg" % (NAME, mode))
+                rgb_tile = its.image.get_image_patch(
+                        rgb_image, 0.45, 0.45, 0.1, 0.1)
+                rgb_snrs = its.image.compute_image_snrs(rgb_tile)
+                rgb_snr_list.append(rgb_snrs)
+
+            r_snrs = [rgb[0] for rgb in rgb_snr_list]
+            g_snrs = [rgb[1] for rgb in rgb_snr_list]
+            b_snrs = [rgb[2] for rgb in rgb_snr_list]
+            rgb_snrs = [numpy.mean(r_snrs), numpy.mean(g_snrs), numpy.mean(b_snrs)]
+            print "NR mode", mode, "SNRs:"
+            print "    R SNR:", rgb_snrs[0],\
+                    "Min:", min(r_snrs), "Max:", max(r_snrs)
+            print "    G SNR:", rgb_snrs[1],\
+                    "Min:", min(g_snrs), "Max:", max(g_snrs)
+            print "    B SNR:", rgb_snrs[2],\
+                    "Min:", min(b_snrs), "Max:", max(b_snrs)
+
+            for chan in range(3):
+                snrs[chan].append(rgb_snrs[chan])
 
     # Draw a plot.
     for j in range(3):
-        pylab.plot(range(5), variances[j], "rgb"[j])
-    matplotlib.pyplot.savefig("%s_plot_variances.png" % (NAME))
+        pylab.plot(range(5), snrs[j], "rgb"[j])
+    matplotlib.pyplot.savefig("%s_plot_SNRs.png" % (NAME))
 
     assert(nr_modes_reported == [0,1,2,3,4])
 
     for j in range(3):
-        # Smaller variance is better
+        # Larger SNR is better
         # Verify OFF(0) is not better than FAST(1)
-        assert(variances[j][0] >
-               variances[j][1] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+        assert(snrs[j][0] <
+               snrs[j][1] + SNR_TOLERANCE)
         # Verify FAST(1) is not better than HQ(2)
-        assert(variances[j][1] >
-               variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+        assert(snrs[j][1] <
+               snrs[j][2] + SNR_TOLERANCE)
         # Verify HQ(2) is better than OFF(0)
-        assert(variances[j][0] > variances[j][2])
+        assert(snrs[j][0] < snrs[j][2])
         if its.caps.noise_reduction_mode(props, 3):
             # Verify OFF(0) is not better than MINIMAL(3)
-            assert(variances[j][0] >
-                   variances[j][3] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+            assert(snrs[j][0] <
+                   snrs[j][3] + SNR_TOLERANCE)
             # Verify MINIMAL(3) is not better than HQ(2)
-            assert(variances[j][3] >
-                   variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+            assert(snrs[j][3] <
+                   snrs[j][2] + SNR_TOLERANCE)
             if its.caps.noise_reduction_mode(props, 4):
                 # Verify ZSL(4) is close to MINIMAL(3)
-                assert(numpy.isclose(variances[j][4], variances[j][3],
-                                     RELATIVE_ERROR_TOLERANCE))
+                assert(numpy.isclose(snrs[j][4], snrs[j][3],
+                                     atol=SNR_TOLERANCE))
         elif its.caps.noise_reduction_mode(props, 4):
             # Verify ZSL(4) is close to OFF(0)
-            assert(numpy.isclose(variances[j][4], variances[j][0],
-                                 RELATIVE_ERROR_TOLERANCE))
+            assert(numpy.isclose(snrs[j][4], snrs[j][0],
+                                 atol=SNR_TOLERANCE))
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
index 6c2b5c1..e176312 100644
--- a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
@@ -44,6 +44,8 @@
 
         # Expose for the scene with min sensitivity
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+        # Digital gains might not be visible on RAW data
+        sens_max = props['android.sensor.maxAnalogSensitivity']
         sens_step = (sens_max - sens_min) / NUM_STEPS
         s_ae,e_ae,_,_,_  = cam.do_3a(get_results=True)
         s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
index 14c5eb0..cc0ce14 100644
--- a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
@@ -42,6 +42,8 @@
 
         # Expose for the scene with min sensitivity
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+        # Digital gains might not be visible on RAW data
+        sens_max = props['android.sensor.maxAnalogSensitivity']
         sens_step = (sens_max - sens_min) / NUM_STEPS
         s_ae,e_ae,_,_,_  = cam.do_3a(get_results=True)
         s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
index 757dfeb..f0a6fbe 100644
--- a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
@@ -38,7 +38,8 @@
 
     NAME = os.path.basename(__file__).split(".")[0]
 
-    RELATIVE_ERROR_TOLERANCE = 0.1
+    NUM_SAMPLES_PER_MODE = 4
+    SNR_TOLERANCE = 3 # unit in db
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -60,7 +61,7 @@
 
         for reprocess_format in reprocess_formats:
             # List of variances for R, G, B.
-            variances = []
+            snrs = [[], [], []]
             nr_modes_reported = []
 
             # NR mode 0 with low gain
@@ -77,71 +78,90 @@
             img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
             its.image.write_image(img, "%s_low_gain_fmt=jpg.jpg" % (NAME))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-            ref_variance = its.image.compute_image_variances(tile)
-            print "Ref variances:", ref_variance
+            ref_snr = its.image.compute_image_snrs(tile)
+            print "Ref SNRs:", ref_snr
 
+            e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
             for nr_mode in range(5):
                 # Skip unavailable modes
                 if not its.caps.noise_reduction_mode(props, nr_mode):
                     nr_modes_reported.append(nr_mode)
-                    variances.append(0)
+                    for channel in range(3):
+                        snrs[channel].append(0)
                     continue
 
-                # NR modes 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"] = nr_mode
-                cap = cam.do_capture(req, out_surface, reprocess_format)
-                nr_modes_reported.append(
-                    cap["metadata"]["android.noiseReduction.mode"])
+                rgb_snr_list = []
+                # Capture several images to account for per frame noise
+                # variations
+                for n in range(NUM_SAMPLES_PER_MODE):
+                    req = its.objects.manual_capture_request(s, e)
+                    req["android.noiseReduction.mode"] = nr_mode
+                    cap = cam.do_capture(req, out_surface, reprocess_format)
 
-                img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
-                its.image.write_image(
-                    img, "%s_high_gain_nr=%d_fmt=jpg.jpg" % (NAME, nr_mode))
-                tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-                # Get the variances for R, G, and B channels
-                variance = its.image.compute_image_variances(tile)
-                variances.append(
-                    [variance[chan] / ref_variance[chan] for chan in range(3)])
-            print "Variances with NR mode [0,1,2,3,4]:", variances
+                    img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+                    if n == 0:
+                        its.image.write_image(
+                                img,
+                                "%s_high_gain_nr=%d_fmt=jpg.jpg"
+                                        %(NAME, nr_mode))
+                        nr_modes_reported.append(
+                                cap["metadata"]["android.noiseReduction.mode"])
+
+                    tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+                    # Get the variances for R, G, and B channels
+                    rgb_snrs = its.image.compute_image_snrs(tile)
+                    rgb_snr_list.append(rgb_snrs)
+
+                r_snrs = [rgb[0] for rgb in rgb_snr_list]
+                g_snrs = [rgb[1] for rgb in rgb_snr_list]
+                b_snrs = [rgb[2] for rgb in rgb_snr_list]
+                rgb_snrs = [numpy.mean(r_snrs),
+                            numpy.mean(g_snrs),
+                            numpy.mean(b_snrs)]
+                print "NR mode", nr_mode, "SNRs:"
+                print "    R SNR:", rgb_snrs[0],\
+                        "Min:", min(r_snrs), "Max:", max(r_snrs)
+                print "    G SNR:", rgb_snrs[1],\
+                        "Min:", min(g_snrs), "Max:", max(g_snrs)
+                print "    B SNR:", rgb_snrs[2],\
+                        "Min:", min(b_snrs), "Max:", max(b_snrs)
+
+                for chan in range(3):
+                    snrs[chan].append(rgb_snrs[chan])
 
             # Draw a plot.
-            for chan in range(3):
-                line = []
-                for nr_mode in range(5):
-                    line.append(variances[nr_mode][chan])
-                pylab.plot(range(5), line, "rgb"[chan])
+            for channel in range(3):
+                pylab.plot(range(5), snrs[channel], "rgb"[channel])
 
-            matplotlib.pyplot.savefig("%s_plot_%s_variances.png" %
+            matplotlib.pyplot.savefig("%s_plot_%s_SNRs.png" %
                                       (NAME, reprocess_format))
 
             assert(nr_modes_reported == [0,1,2,3,4])
 
             for j in range(3):
-                # Smaller variance is better
+                # Larger is better
                 # Verify OFF(0) is not better than FAST(1)
-                assert(variances[0][j] >
-                       variances[1][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                assert(snrs[j][0] <
+                       snrs[j][1] + SNR_TOLERANCE)
                 # Verify FAST(1) is not better than HQ(2)
-                assert(variances[1][j] >
-                       variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                assert(snrs[j][1] <
+                       snrs[j][2] + SNR_TOLERANCE)
                 # Verify HQ(2) is better than OFF(0)
-                assert(variances[0][j] > variances[2][j])
+                assert(snrs[j][0] < snrs[j][2])
                 if its.caps.noise_reduction_mode(props, 3):
                     # Verify OFF(0) is not better than MINIMAL(3)
-                    assert(variances[0][j] >
-                           variances[3][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                    assert(snrs[j][0] <
+                           snrs[j][3] + SNR_TOLERANCE)
                     # Verify MINIMAL(3) is not better than HQ(2)
-                    assert(variances[3][j] >
-                           variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                    assert(snrs[j][3] <
+                           snrs[j][2] + SNR_TOLERANCE)
                     # Verify ZSL(4) is close to MINIMAL(3)
-                    assert(numpy.isclose(variances[4][j], variances[3][j],
-                                         RELATIVE_ERROR_TOLERANCE))
+                    assert(numpy.isclose(snrs[j][4], snrs[j][3],
+                                         atol=SNR_TOLERANCE))
                 else:
                     # Verify ZSL(4) is close to OFF(0)
-                    assert(numpy.isclose(variances[4][j], variances[0][j],
-                                         RELATIVE_ERROR_TOLERANCE))
+                    assert(numpy.isclose(snrs[j][4], snrs[j][0],
+                                         atol=SNR_TOLERANCE))
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
index 33e7763..268b64a 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
@@ -31,6 +31,12 @@
         cam.do_3a()
 
         req = its.objects.auto_capture_request()
+        max_dng_size = \
+                its.objects.get_available_output_sizes("raw", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_dng_size)[0]
+        out_surfaces = [{"format":"dng"},
+                        {"format":"yuv", "width":w, "height":h}]
         cap_dng, cap_yuv = cam.do_capture(req, cam.CAP_DNG_YUV)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
index 9ce8d76..78378eb 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
@@ -27,13 +27,17 @@
 
     THRESHOLD_MAX_RMS_DIFF = 0.01
 
-    fmt_yuv =  {"format":"yuv"}
-    fmt_jpeg = {"format":"jpeg"}
-
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         its.caps.skip_unless(its.caps.compute_target_exposure(props))
 
+        max_jpeg_size = \
+                its.objects.get_available_output_sizes("jpeg", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_jpeg_size)[0]
+        fmt_yuv =  {"format":"yuv", "width":w, "height":h}
+        fmt_jpeg = {"format":"jpeg"}
+
         # Use a manual request with a linear tonemap so that the YUV and JPEG
         # should look the same (once converted by the its.image module).
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index f13801b..bfa6a28 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -38,7 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True, props)
 
-        cap_raw, cap_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
+        max_raw_size = \
+                its.objects.get_available_output_sizes("raw", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw_size)[0]
+        out_surfaces = [{"format":"raw"},
+                        {"format":"yuv", "width":w, "height":h}]
+        cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index e52946d..322af10 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -38,8 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True, props)
 
+        max_raw10_size = \
+                its.objects.get_available_output_sizes("raw10", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw10_size)[0]
         cap_raw, cap_yuv = cam.do_capture(req,
-                [{"format":"raw10"}, {"format":"yuv"}])
+                [{"format":"raw10"},
+                 {"format":"yuv", "width":w, "height":h}])
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
index c5c3c73..b3cca0b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
@@ -38,8 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True, props)
 
+        max_raw12_size = \
+                its.objects.get_available_output_sizes("raw12", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw12_size)[0]
         cap_raw, cap_yuv = cam.do_capture(req,
-                [{"format":"raw12"}, {"format":"yuv"}])
+                [{"format":"raw12"},
+                 {"format":"yuv", "width":w, "height":h}])
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index 73834cb..e96a9ee 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -53,22 +53,28 @@
     """
 
     NAME = os.path.basename(__file__).split(".")[0]
+    NUM_SAMPLES = 4
 
     req = its.objects.manual_capture_request(sensitivity, exp)
     req["android.lens.focusDistance"] = fd
     req["android.edge.mode"] = edge_mode
     if (reprocess_format != None):
         req["android.reprocess.effectiveExposureFactor"] = 1.0
-    cap = cam.do_capture(req, out_surface, reprocess_format)
 
-    img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
-    its.image.write_image(img, "%s_edge=%d_reprocess_fmt_%s.jpg" %
-        (NAME, edge_mode, reprocess_format))
-    tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+    sharpness_list = []
+    for n in range(NUM_SAMPLES):
+        cap = cam.do_capture(req, out_surface, reprocess_format)
+        img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+        if n == 0:
+            its.image.write_image(img, "%s_reprocess_fmt_%s_edge=%d.jpg" %
+                (NAME, reprocess_format, edge_mode))
+            res_edge_mode = cap["metadata"]["android.edge.mode"]
+        tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+        sharpness_list.append(its.image.compute_image_sharpness(tile))
 
     ret = {}
-    ret["edge_mode"] = cap["metadata"]["android.edge.mode"]
-    ret["sharpness"] = its.image.compute_image_sharpness(tile)
+    ret["edge_mode"] = res_edge_mode
+    ret["sharpness"] = numpy.mean(sharpness_list)
 
     return ret
 
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 8cec7ea..34246cc 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -41,7 +41,7 @@
 
 LOCAL_PACKAGE_NAME := CtsVerifier
 
-LOCAL_AAPT_FLAGS += --version-name "6.0_r0 $(BUILD_NUMBER)"
+LOCAL_AAPT_FLAGS += --version-name "6.0_r1 $(BUILD_NUMBER)"
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni libaudioloopback_jni
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index d3dcf07..ea16821 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -754,7 +754,7 @@
             android:screenOrientation="locked" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST_disabled"/>
             </intent-filter>
 
             <meta-data
@@ -763,8 +763,6 @@
             <meta-data
                 android:name="test_required_features"
                 android:value="android.hardware.sensor.accelerometer:android.hardware.sensor.gyroscope:android.hardware.sensor.compass:android.hardware.camera.any" />
-            <meta-data android:name="test_excluded_features"
-                    android:value="android.hardware.type.television" />
         </activity>
         <activity
             android:name=".sensors.RVCVRecordActivity"
@@ -1398,6 +1396,13 @@
             </intent-filter>
         </activity-alias>
 
+        <activity android:name=".managedprovisioning.AuthenticationBoundKeyTestActivity">
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".managedprovisioning.ByodFlowTestActivity"
                 android:launchMode="singleTask"
                 android:label="@string/provisioning_byod">
@@ -1454,19 +1459,88 @@
 
         <activity android:name=".managedprovisioning.CrossProfileTestActivity">
             <intent-filter>
-                <!-- We need to have at least one activity listening to this intent in the parent
-                     to test if it is forwarded from the managed profile to the parent -->
-                <action android:name="android.provider.MediaStore.RECORD_SOUND" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_PERSONAL" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_WORK" />
-                <category android:name="android.intent.category.DEFAULT"></category>
+                <!-- We need to have at least one activity listening to these intents on the device
+                     to test if these are forwarded from the managed profile to the parent or
+                     the other way around. -->
+                <action android:name="android.provider.MediaStore.RECORD_SOUND" />
+                <action android:name="android.speech.action.RECOGNIZE_SPEECH" />
+                <action android:name="android.app.action.SET_NEW_PASSWORD" />
+                <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
+                <action android:name="android.intent.action.WEB_SEARCH" />
+                <action android:name="android.intent.action.VIEW_DOWNLOADS" />
+                <action android:name="android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL" />
+                <action android:name="android.settings.SHOW_INPUT_METHOD_PICKER" />
+                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
+                <action android:name="com.android.settings.TTS_SETTINGS" />
+                <action android:name="android.settings.ZEN_MODE_SETTINGS" />
+                <action android:name="android.settings.BATTERY_SAVER_SETTINGS" />
+                <action android:name="android.settings.INPUT_METHOD_SETTINGS" />
+                <action android:name="android.settings.INPUT_METHOD_SUBTYPE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <action android:name="android.intent.action.SEND_MULTIPLE" />
+                <data android:mimeType="*/*" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.GET_CONTENT" />
+                <action android:name="android.intent.action.OPEN_DOCUMENT" />
+                <data android:mimeType="*/*" />
+                <category android:name="android.intent.category.OPENABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <action android:name="android.intent.action.SENDTO" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="sms" />
+                <data android:scheme="smsto" />
+                <data android:scheme="mms" />
+                <data android:scheme="mmsto" />
+                <data android:scheme="mailto" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <action android:name="android.intent.action.CALL" />
+                <action android:name="android.intent.action.DIAL" />
+                <action android:name="android.intent.action.CALL_PRIVILEGED" />
+                <action android:name="android.intent.action.CALL_EMERGENCY" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="tel" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.INSERT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="content" />
+                <data android:mimeType="*/*" />
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT"></category>
+                <category android:name="android.intent.category.DEFAULT" />
                 <data android:scheme="http" android:host="com.android.cts.verifier" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="http" />
+                <data android:mimeType="video/mp4" />
+                <data android:mimeType="audio/*" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="http" />
+                <data android:scheme="geo" />
+                <data android:scheme="market" />
+            </intent-filter>
         </activity>
 
         <activity android:name=".managedprovisioning.WorkStatusTestActivity">
@@ -1583,6 +1657,18 @@
                     android:value="android.software.live_tv" />
         </activity>
 
+        <activity android:name=".tv.AppLinkTestActivity"
+            android:label="@string/tv_app_link_test"
+            android:launchMode="singleTask">
+            <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_tv" />
+            <meta-data android:name="test_required_features"
+                android:value="android.software.live_tv" />
+        </activity>
+
         <activity android:name=".screenpinning.ScreenPinningTestActivity"
             android:label="@string/screen_pinning_test">
             <intent-filter>
@@ -1622,8 +1708,8 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
         </activity>
 
-        <activity android:name=".audio.AudioDeviceNotificationsActivity"
-                  android:label="@string/audio_devices_notifications_test">
+        <activity android:name=".audio.AudioOutputDeviceNotificationsActivity"
+                  android:label="@string/audio_in_devices_notifications_test">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
@@ -1632,18 +1718,37 @@
             <!--
             <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
             -->
+            <meta-data android:name="test_required_features" android:value="android.hardware.audio.output" />
         </activity>
 
-        <activity android:name=".audio.AudioRoutingNotificationsActivity"
-                  android:label="@string/audio_routingnotifications_test">
+        <activity android:name=".audio.AudioInputDeviceNotificationsActivity"
+                  android:label="@string/audio_in_devices_notifications_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_audio" />
-            <!--
             <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
-            -->
+        </activity>
+
+        <activity android:name=".audio.AudioOutputRoutingNotificationsActivity"
+                  android:label="@string/audio_output_routingnotifications_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_audio" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.audio.output" />
+        </activity>
+
+        <activity android:name=".audio.AudioInputRoutingNotificationsActivity"
+                  android:label="@string/audio_input_routingnotifications_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_audio" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
         </activity>
 
         <activity android:name=".audio.AudioLoopbackActivity"
@@ -1654,6 +1759,7 @@
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_audio" />
             <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.audio.output" />
             <meta-data android:name="test_excluded_features" android:value="android.hardware.type.watch" />
             <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television" />
         </activity>
diff --git a/apps/CtsVerifier/jni/verifier/Android.mk b/apps/CtsVerifier/jni/verifier/Android.mk
index da4687d..4840e62 100644
--- a/apps/CtsVerifier/jni/verifier/Android.mk
+++ b/apps/CtsVerifier/jni/verifier/Android.mk
@@ -25,8 +25,11 @@
 
 LOCAL_SRC_FILES := \
 		CtsVerifierJniOnLoad.cpp \
-		com_android_cts_verifier_os_FileUtils.cpp	
+		com_android_cts_verifier_camera_StatsImage.cpp \
+		com_android_cts_verifier_os_FileUtils.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
+LOCAL_SHARED_LIBRARIES := liblog
+
 include $(BUILD_SHARED_LIBRARY)
diff --git a/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp b/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
index 81e5690..399275b 100644
--- a/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
+++ b/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright (C) 2010 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
 #include <stdio.h>
 
 extern int register_com_android_cts_verifier_os_FileUtils(JNIEnv*);
+extern int register_com_android_cts_verifier_camera_its_StatsImage(JNIEnv*);
 
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env = NULL;
@@ -30,5 +31,9 @@
         return JNI_ERR;
     }
 
+    if (register_com_android_cts_verifier_camera_its_StatsImage(env)) {
+        return JNI_ERR;
+    }
+
     return JNI_VERSION_1_4;
 }
diff --git a/apps/CtsVerifier/jni/verifier/com_android_cts_verifier_camera_StatsImage.cpp b/apps/CtsVerifier/jni/verifier/com_android_cts_verifier_camera_StatsImage.cpp
new file mode 100644
index 0000000..16dff85
--- /dev/null
+++ b/apps/CtsVerifier/jni/verifier/com_android_cts_verifier_camera_StatsImage.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ITS-StatsImage-JNI"
+// #define LOG_NDEBUG 0
+#include <android/log.h>
+#include <utils/Log.h>
+
+#include <jni.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <string.h>
+
+jfloatArray com_android_cts_verifier_camera_its_computeStatsImage(JNIEnv* env, jobject thiz,
+        jbyteArray img, jint width, jint height, jint gridWidth, jint gridHeight)
+{
+    int bufSize = (int)(env->GetArrayLength(img));
+    unsigned char *buf = (unsigned char*)env->GetByteArrayElements(img, /*is_copy*/NULL);
+
+    // Size of the raw image.
+    const int w = width;
+    const int h = height;
+    // Size of each grid cell.
+    const int gw = gridWidth;
+    const int gh = gridHeight;
+    // Number of grid cells (rounding down to full cells only at right+bottom edges).
+    const int ngx = w / gw;
+    const int ngy = h / gh;
+
+    float *mean = new float[ngy*ngx*4];
+    float *var = new float[ngy*ngx*4];
+    for (int gy = 0; gy < ngy; gy++) {
+        for (int gx = 0; gx < ngx; gx++) {
+            float sum[4] = {0};
+            float sumSq[4] = {0};
+            int count[4] = {0};
+            for (int y = gy*gh; y < (gy+1)*gh; y++) {
+                int chnOffset = (y & 0x1) * 2;
+                unsigned char *pbuf = buf + 2*y*w + 2*gx*gw;
+                for (int x = gx*gw; x < (gx+1)*gw; x++) {
+                    // input is RAW16
+                    int byte0 = *pbuf++;
+                    int byte1 = *pbuf++;
+                    int pixelValue = (byte1 << 8) | byte0;
+                    int ch = chnOffset + (x & 1);
+                    sum[ch] += pixelValue;
+                    sumSq[ch] += pixelValue * pixelValue;
+                    count[ch] += 1;
+                }
+            }
+            for (int ch = 0; ch < 4; ch++) {
+                float m = (float)sum[ch] / count[ch];
+                float mSq = (float)sumSq[ch] / count[ch];
+                mean[gy*ngx*4 + gx*4 + ch] = m;
+                var[gy*ngx*4 + gx*4 + ch] = mSq - m*m;
+            }
+        }
+    }
+
+    jfloatArray ret = env->NewFloatArray(ngx*ngy*4*2);
+    env->SetFloatArrayRegion(ret, 0, ngx*ngy*4, (float*)mean);
+    env->SetFloatArrayRegion(ret, ngx*ngy*4, ngx*ngy*4, (float*)var);
+    delete [] mean;
+    delete [] var;
+    return ret;
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "computeStatsImage", "([BIIII)[F",
+            (void *) com_android_cts_verifier_camera_its_computeStatsImage  },
+};
+
+int register_com_android_cts_verifier_camera_its_StatsImage(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("com/android/cts/verifier/camera/its/StatsImage");
+
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/apps/CtsVerifier/res/drawable/app_link_img.png b/apps/CtsVerifier/res/drawable/app_link_img.png
new file mode 100644
index 0000000..851fc6f
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable/app_link_img.png
Binary files differ
diff --git a/apps/CtsVerifier/res/layout/audio_dev_notify.xml b/apps/CtsVerifier/res/layout/audio_dev_notify.xml
index 98dbd8b..0975ab9 100644
--- a/apps/CtsVerifier/res/layout/audio_dev_notify.xml
+++ b/apps/CtsVerifier/res/layout/audio_dev_notify.xml
@@ -24,8 +24,7 @@
       android:layout_height="wrap_content"
       android:scrollbars="vertical"
       android:gravity="bottom"
-      android:id="@+id/info_text"
-      android:text="@string/audio_devices_notification_instructions" />
+      android:id="@+id/info_text"/>
 
   <LinearLayout
       android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml
index 69e3bc7..c1b62af 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml
@@ -13,60 +13,105 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="10dip"
+    android:orientation="vertical"
+>
+    <ScrollView
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:padding="10dip"
-        android:orientation="vertical">
+        android:layout_height="match_parent"
+        android:id="@+id/scrollView"
+    >
 
-  <TextView
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:scrollbars="vertical"
-      android:gravity="bottom"
-      android:id="@+id/info_text"
-      android:text="@string/audio_frequency_line_instructions" />
-  <LinearLayout
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:orientation="vertical">
-      <Button
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:id="@+id/audio_frequency_line_plug_ready_btn"
-          android:text="@string/audio_frequency_line_plug_ready_btn"/>
-
-    <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-     <LinearLayout
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:orientation="horizontal"
-          android:id="@+id/audio_frequency_line_layout">
-            <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/audio_frequency_line_test_btn"
-                android:id="@+id/audio_frequency_line_test_btn"/>
-
-            <ProgressBar
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:id="@+id/audio_frequency_line_progress_bar"/>
-        </LinearLayout>
-
-        <TextView
-            android:layout_width="wrap_content"
+        <LinearLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:text="@string/audio_frequency_line_results_text"
-            android:id="@+id/audio_frequency_line_results_text"/>
+            android:orientation="vertical"
+        >
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:scrollbars="vertical"
+                android:gravity="bottom"
+                android:id="@+id/audio_general_headset_port_exists"
+                android:text="@string/audio_general_headset_port_exists" />
 
-    </LinearLayout>
-    </LinearLayout>
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+            >
 
-  <include layout="@layout/pass_fail_buttons" />
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_general_headset_no"
+                    android:text="@string/audio_general_headset_no" />
+
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_general_headset_yes"
+                    android:text="@string/audio_general_headset_yes" />
+
+            </LinearLayout>
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:scrollbars="vertical"
+                android:gravity="bottom"
+                android:id="@+id/info_text"
+                android:text="@string/audio_frequency_line_instructions" />
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+            >
+                <Button
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_frequency_line_plug_ready_btn"
+                    android:text="@string/audio_frequency_line_plug_ready_btn" />
+
+                <LinearLayout
+                    android:orientation="vertical"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                >
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="horizontal"
+                        android:id="@+id/audio_frequency_line_layout"
+                    >
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/audio_frequency_line_test_btn"
+                            android:id="@+id/audio_frequency_line_test_btn" />
+
+                        <ProgressBar
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:id="@+id/audio_frequency_line_progress_bar" />
+                    </LinearLayout>
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/audio_frequency_line_results_text"
+                        android:id="@+id/audio_frequency_line_results_text" />
+
+                </LinearLayout>
+            </LinearLayout>
+
+            <include layout="@layout/pass_fail_buttons" />
+        </LinearLayout>
+    </ScrollView>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
index 10b0003..db52998 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
@@ -18,116 +18,151 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:padding="10dip"
-    android:orientation="vertical">
+    android:orientation="vertical"
+>
 
     <ScrollView
-       android:layout_width="match_parent"
-       android:layout_height="match_parent"
-       android:id="@+id/scrollView">
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/scrollView"
+    >
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical">
+            android:orientation="vertical"
+        >
 
-        <LinearLayout
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:scrollbars="vertical"
+                android:gravity="bottom"
+                android:id="@+id/audio_general_headset_port_exists"
+                android:text="@string/audio_general_headset_port_exists" />
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+            >
+
+                <Button
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:orientation="horizontal">
-          <TextView
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:layout_weight="1"
-              android:id="@+id/info_text"
-              android:text="@string/audio_frequency_mic_instructions"/>
+                    android:id="@+id/audio_general_headset_no"
+                    android:text="@string/audio_general_headset_no" />
 
-           <ProgressBar
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_weight="1"
-                        android:id="@+id/audio_frequency_mic_progress_bar"/>
-         </LinearLayout>
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_general_headset_yes"
+                    android:text="@string/audio_general_headset_yes" />
 
-          <Button
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:id="@+id/audio_frequency_mic_speakers_ready_btn"
-              android:text="@string/audio_frequency_mic_speakers_ready_btn"/>
+            </LinearLayout>
 
-          <TextView
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+            >
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:id="@+id/info_text"
+                    android:text="@string/audio_frequency_mic_instructions" />
+
+                <ProgressBar
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:id="@+id/audio_frequency_mic_progress_bar" />
+            </LinearLayout>
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:id="@+id/audio_frequency_mic_speakers_ready_btn"
+                android:text="@string/audio_frequency_mic_speakers_ready_btn" />
+
+            <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:scrollbars="vertical"
                 android:gravity="bottom"
                 android:id="@+id/audio_frequency_mic_speakers_ready_status"
-                android:text="@string/audio_frequency_mic_speakers_ready_status"/>
+                android:text="@string/audio_frequency_mic_speakers_ready_status" />
 
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:orientation="vertical"
-                android:id="@+id/audio_frequency_mic_layout_test1">
+                android:id="@+id/audio_frequency_mic_layout_test1"
+            >
 
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/audio_frequency_mic_instructions2"
-                android:id="@+id/audio_frequency_mic_instructions2"/>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/audio_frequency_mic_instructions2"
+                    android:id="@+id/audio_frequency_mic_instructions2" />
 
-              <Button
+                <Button
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="@string/audio_frequency_mic_test1_btn"
-                    android:id="@+id/audio_frequency_mic_test1_btn"/>
+                    android:id="@+id/audio_frequency_mic_test1_btn" />
 
                 <TextView
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="@string/audio_frequency_mic_results_text"
-                    android:id="@+id/audio_frequency_mic_results1_text"/>
-          </LinearLayout>
+                    android:id="@+id/audio_frequency_mic_results1_text" />
+            </LinearLayout>
 
-          <LinearLayout
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical"
-                android:id="@+id/audio_frequency_mic_layout_test2a">
+                android:id="@+id/audio_frequency_mic_layout_test2a"
+            >
 
-            <Button
+                <Button
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_frequency_mic_mic_ready_btn"
+                    android:text="@string/audio_frequency_mic_mic_ready_btn" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/audio_frequency_mic_usb_status"
+                    android:id="@+id/audio_frequency_mic_usb_status" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:orientation="vertical"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:id="@+id/audio_frequency_mic_mic_ready_btn"
-                android:text="@string/audio_frequency_mic_mic_ready_btn"/>
+                android:layout_height="match_parent"
+                android:id="@+id/audio_frequency_mic_layout_test2b"
+            >
 
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/audio_frequency_mic_usb_status"
-                android:id="@+id/audio_frequency_mic_usb_status"/>
-           </LinearLayout>
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/audio_frequency_mic_test2_btn"
+                    android:id="@+id/audio_frequency_mic_test2_btn" />
 
-          <LinearLayout
-              android:orientation="vertical"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:id="@+id/audio_frequency_mic_layout_test2b">
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/audio_frequency_mic_results_text"
+                    android:id="@+id/audio_frequency_mic_results_text" />
 
-            <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/audio_frequency_mic_test2_btn"
-                android:id="@+id/audio_frequency_mic_test2_btn"/>
+            </LinearLayout>
 
-              <TextView
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:text="@string/audio_frequency_mic_results_text"
-                  android:id="@+id/audio_frequency_mic_results_text"/>
-
+            <include layout="@layout/pass_fail_buttons" />
         </LinearLayout>
-
-        <include layout="@layout/pass_fail_buttons"/>
-        </LinearLayout>
-      </ScrollView>
+    </ScrollView>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml b/apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
similarity index 64%
copy from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
copy to apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
index cef30d6..ca7dd19 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
@@ -26,40 +26,7 @@
       android:scrollbars="vertical"
       android:gravity="bottom"
       android:id="@+id/info_text"
-      android:text="@string/audio_dev_routingnotification_instructions" />
-
-  <LinearLayout
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:orientation="vertical"
-      android:id="@+id/audioTrackRoutingLayout">
-      <TextView
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-              android:text="@string/audio_routingnotification_playHeader"/>
-
-      <TextView
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:id="@+id/audio_routingnotification_audioTrack_change"/>
-
-      <LinearLayout
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:orientation="horizontal">
-          <Button
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:id="@+id/audio_routingnotification_playBtn"
-              android:text="@string/audio_routingnotification_playBtn"/>
-
-          <Button
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:id="@+id/audio_routingnotification_playStopBtn"
-              android:text="@string/audio_routingnotification_playStopBtn"/>
-      </LinearLayout>
-    </LinearLayout>
+      android:text="@string/audio_input_routingnotification_instructions" />
 
   <LinearLayout
       android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_activity.xml b/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
index 626ac4f..815f2bc 100644
--- a/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
@@ -13,71 +13,120 @@
      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="wrap_content"
-        android:padding="10dip"
-        android:orientation="vertical">
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="10dip"
+    android:orientation="vertical"
+>
 
-  <TextView
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:scrollbars="vertical"
-      android:gravity="bottom"
-      android:id="@+id/info_text"
-      android:text="@string/audio_loopback_instructions" />
-  <LinearLayout
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:orientation="vertical">
-      <Button
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:id="@+id/audio_loopback_plug_ready_btn"
-          android:text="@string/audio_loopback_plug_ready_btn"/>
-
-    <LinearLayout
-        android:orientation="vertical"
+    <ScrollView
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:id="@+id/audio_loopback_layout">
+        android:id="@+id/scrollView"
+    >
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/audio_loopback_instructions2"
-            android:id="@+id/audio_loopback_instructions2"/>
-
-        <SeekBar
+        <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:id="@+id/audio_loopback_level_seekbar"/>
+            android:orientation="vertical"
+        >
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/audio_loopback_level_text"
-            android:id="@+id/audio_loopback_level_text"/>
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:scrollbars="vertical"
+                android:gravity="bottom"
+                android:id="@+id/audio_general_headset_port_exists"
+                android:text="@string/audio_general_headset_port_exists" />
 
-        <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/audio_loopback_test_btn"
-            android:id="@+id/audio_loopback_test_btn"/>
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+            >
 
-        <ProgressBar
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/audio_loopback_progress_bar"/>
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_general_headset_no"
+                    android:text="@string/audio_general_headset_no" />
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/audio_loopback_results_text"
-            android:id="@+id/audio_loopback_results_text"/>
-    </LinearLayout>
-    </LinearLayout>
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_general_headset_yes"
+                    android:text="@string/audio_general_headset_yes" />
 
-  <include layout="@layout/pass_fail_buttons" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:id="@+id/audio_loopback_headset_port"
+            >
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:scrollbars="vertical"
+                    android:gravity="bottom"
+                    android:id="@+id/info_text"
+                    android:text="@string/audio_loopback_instructions" />
+
+                <Button
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/audio_loopback_plug_ready_btn"
+                    android:text="@string/audio_loopback_plug_ready_btn" />
+
+                <LinearLayout
+                    android:orientation="vertical"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:id="@+id/audio_loopback_layout"
+                >
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/audio_loopback_instructions2"
+                        android:id="@+id/audio_loopback_instructions2" />
+
+                    <SeekBar
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:id="@+id/audio_loopback_level_seekbar" />
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/audio_loopback_level_text"
+                        android:id="@+id/audio_loopback_level_text" />
+
+                    <Button
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/audio_loopback_test_btn"
+                        android:id="@+id/audio_loopback_test_btn" />
+
+                    <ProgressBar
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:id="@+id/audio_loopback_progress_bar" />
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/audio_loopback_results_text"
+                        android:id="@+id/audio_loopback_results_text" />
+                </LinearLayout>
+
+            </LinearLayout>
+            <include layout="@layout/pass_fail_buttons" />
+        </LinearLayout>
+    </ScrollView>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml b/apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
similarity index 64%
rename from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
rename to apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
index cef30d6..b321000 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
@@ -26,7 +26,7 @@
       android:scrollbars="vertical"
       android:gravity="bottom"
       android:id="@+id/info_text"
-      android:text="@string/audio_dev_routingnotification_instructions" />
+      android:text="@string/audio_output_routingnotification_instructions" />
 
   <LinearLayout
       android:layout_width="match_parent"
@@ -61,39 +61,6 @@
       </LinearLayout>
     </LinearLayout>
 
-  <LinearLayout
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:orientation="vertical"
-      android:id="@+id/audioRecordRoutingLayout">
-      <TextView
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-              android:text="@string/audio_routingnotification_recHeader"/>
-
-      <TextView
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:id="@+id/audio_routingnotification_audioRecord_change"/>
-
-      <LinearLayout
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:orientation="horizontal">
-          <Button
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:id="@+id/audio_routingnotification_recordBtn"
-              android:text="@string/audio_routingnotification_recBtn"/>
-
-          <Button
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:id="@+id/audio_routingnotification_recordStopBtn"
-              android:text="@string/audio_routingnotification_recStopBtn"/>
-      </LinearLayout>
-    </LinearLayout>
-
   <include layout="@layout/pass_fail_buttons" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/keychain_main.xml b/apps/CtsVerifier/res/layout/keychain_main.xml
index 01eb255..3f695cd 100644
--- a/apps/CtsVerifier/res/layout/keychain_main.xml
+++ b/apps/CtsVerifier/res/layout/keychain_main.xml
@@ -24,22 +24,36 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
-        android:padding="10dip" >
+        android:padding="10dip">
 
-        <TextView
-            android:id="@+id/test_instruction"
-            style="@style/InstructionsFont"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="0" />
-
-        <TextView
-            android:id="@+id/test_log"
+        <ScrollView
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_weight="1"
-            android:layout_gravity="bottom"
-            android:orientation="vertical" />
+            android:fillViewport="true">
+
+            <LinearLayout
+                android:id="@+id/test_messages"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <TextView
+                    android:id="@+id/test_instruction"
+                    style="@style/InstructionsFont"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="0" />
+
+                <TextView
+                    android:id="@+id/test_log"
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"
+                    android:orientation="vertical" />
+
+            </LinearLayout>
+        </ScrollView>
 
         <LinearLayout
             android:id="@+id/action_buttons"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 219a8b3..d8a96eb 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1277,7 +1277,25 @@
     <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>
+    <string name="provisioning_byod_capture_image_error">Error while capturing image from managed profile.</string>
 
+    <string name="provisioning_byod_auth_bound_key">Autentication-boud keys</string>
+    <string name="provisioning_byod_auth_bound_key_info">
+        This test verifies keystore cryptographic keys can be bound to device credentials.
+        These keys should only be available if there was a recent enough authentication.
+    </string>
+    <string name="provisioning_byod_auth_bound_key_instruction">
+        This test verifies keystore cryptographic keys can be bound to device lockscreen challenge or fingerprints (if available).
+        These keys should only be available if there was a recent enough authentication. \n
+
+        1. Press "Set up" to open Security settings. Create a lockscreen password and if available, enroll a fingerprint.\n
+        2. Go through the list of tests.\n
+        3. Mark the overall test pass or fail.\n
+        4. Once the set of tests are completed, remove the lockscreen challenge.
+    </string>
+    <string name="provisioning_byod_auth_bound_key_set_up">Set up</string>
+    <string name="provisioning_byod_lockscreen_bound_key">Lockscreen-bound key test</string>
+    <string name="provisioning_byod_fingerprint_bound_key">Fingerprint-bound key test</string>
     <!-- Strings for DeskClock -->
     <string name="deskclock_tests">Alarms and Timers Tests</string>
     <string name="deskclock_tests_info">
@@ -1660,6 +1678,7 @@
     <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="test_failed_cannot_start_intent">Cannot start the given intent.</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>
@@ -1895,7 +1914,6 @@
     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">TV app parental controls test</string>
     <string name="tv_parental_control_test_info">
@@ -1984,6 +2002,24 @@
     The playback position should be moved to the next position.
     </string>
 
+    <string name="tv_app_link_test">TV app app-link test</string>
+    <string name="tv_app_link_test_info">
+    Verify that the bundled TV app supports linking to channel apps. If TV input service provides
+    links for its specific channels, TV app should show the links in a proper format.
+    </string>
+    <string name="tv_app_link_test_select_app_link">
+    Select the \"Launch TV app\" button, then check if you can see a menu with \"Cts App-Link Text\"
+    text in red background. If you see the link, select it to follow the link.
+    </string>
+    <string name="tv_app_link_test_verify_link_clicked">
+    The app-link must have been clicked and the activity should be changed correctly.
+    </string>
+    <string name="tv_input_link_test_verify_link_interface">
+    Do you see the app-link card similar to the image on the left?\n
+    1) You should see the poster art image, but the color could be differ.\n
+    2) You should see the text \"Cts App-Link Text\".\n
+    </string>
+
     <string name="overlay_view_text">Overlay View Dummy Text</string>
     <string name="fake_rating">Fake</string>
 
@@ -2004,25 +2040,34 @@
     <string name="error_screen_pinning_did_not_exit">Screen was not unpinned.</string>
     <string name="error_screen_pinning_couldnt_exit">Could not exit screen pinning through API.</string>
 
-    <!--  Audio Devices Notifcations Test -->
-    <string name="audio_devices_notifications_test">Audio Devices Notifications Test</string>
-    <string name="audio_devices_notification_instructions">
+    <!--  Audio Devices Notifcations Tests -->
+    <string name="audio_out_devices_notifications_test">Audio Output Devices Notifications Test</string>
+    <string name="audio_out_devices_notification_instructions">
           Click the "Clear Messages" button then connect and disconnect a wired headset.
           Note if the appropriate notification messages appear below.
     </string>
+    <string name="audio_in_devices_notifications_test">Audio Input Devices Notifications Test</string>
+    <string name="audio_in_devices_notification_instructions">
+          Click the "Clear Messages" button then connect and disconnect a microphone or wired headset.
+          Note if the appropriate notification messages appear below.
+    </string>
     <string name="audio_dev_notification_clearmsgs">Clear Messages</string>
     <string name="audio_dev_notification_connectMsg">CONNECT DETECTED</string>
     <string name="audio_dev_notification_disconnectMsg">DISCONNECT DETECTED</string>
 
-    <!--  Audio Routing Notifcations Test -->
-    <string name="audio_routingnotifications_test">Audio Routing Notifications Test</string>
-    <string name="audio_dev_routingnotification_instructions">
-          Click on the "Play" button in the AudioTrack Routing Notifictions section below to
+    <string name="audio_input_routingnotifications_test">Audio Input Routing Notifications Test</string>
+    <string name="audio_input_routingnotification_instructions">
+          Click on the "Record" button in the AudioRecord Routing Notifications section below to
+          start recording. Insert a wired headset or microphone. Observe a message acknowledging the
+          rerouting event below. Remove the wired headset and observe the new routing message.
+          Click on the "Stop" button to stop recording.\n
+    </string>
+    <string name="audio_output_routingnotifications_test">Audio Output Routing Notifications Test</string>
+    <string name="audio_output_routingnotification_instructions">
+          Click on the "Play" button in the AudioTrack Routing Notifications section below to
           start (silent) playback. Insert a wired headset. Observe a message acknowledging the
           rerouting event below. Remove the wired headset and observe the new routing message.
           Click on the "Stop" button to stop playback.\n
-          Repeat the process with "Record" and "Stop" button in the AudioRecord Routing
-          Notifications section below.
     </string>
     <string name="audio_routingnotification_playBtn">Play</string>
     <string name="audio_routingnotification_playStopBtn">Stop</string>
@@ -2033,14 +2078,19 @@
     <string name="audio_routingnotification_trackRoutingMsg">AudioTrack rerouting</string>
     <string name="audio_routingnotification_recordRoutingMsg">AudioRecord rerouting</string>
 
+    <!-- Audio general text -->
+    <string name="audio_general_headset_port_exists">Does this device have a headset port?</string>
+    <string name="audio_general_headset_no">No</string>
+    <string name="audio_general_headset_yes">Yes</string>
+
     <!-- Audio Loopback Latency Test -->
     <string name="audio_loopback_test">Audio Loopback Latency Test</string>
      <string name="audio_loopback_info">
-          This test requires the Loopback Plug. Please connect a Loopback Plug on the headset
+          This test requires the Loopback Plug. Please connect a Loopback Plug into the headset
           connector, and proceed with the instructions on the screen.
           The system will measure the input-output audio latency by injecting a pulse on the output,
           and computing the distance between replicas of the pulse.
-          You can vary the Audio Level slider to ensure the pulse will feed back at adecuate levels.
+          You can vary the Audio Level slider to ensure the pulse will feed back at adequate levels.
           Repeat until a confidence level >= 0.6 is achieved.
     </string>
     <string name="audio_loopback_instructions">
@@ -2075,7 +2125,7 @@
     <string name="audio_frequency_speaker_test">Audio Frequency Speaker Test</string>
     <string name="audio_frequency_speaker_info">
         This test requires an external USB reference microphone. Please connect the USB microphone and proceed with the instructions on the screen.
-        The system will measure frequency response of the left and right speakers (if there are two speakers), or twice the response of the mono speaker.
+        The system will measure frequency response of the left and right speakers (if there are two speakers), or the response of the mono speaker twice.
        </string>
     <string name="audio_frequency_speaker_instructions">
           Please connect an USB reference microphone and press "USB Reference microphone ready"
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
index 789effa..91b8d93 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.database.DataSetObserver;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -41,6 +42,7 @@
  * Instructions are shown on top of the screen and a test preparation button is provided.
  */
 public abstract class DialogTestListActivity extends PassFailButtons.TestListActivity {
+    private final String TAG = "DialogTestListActivity";
     private final int mLayoutId;
     private final int mTitleStringId;
     private final int mInfoStringId;
@@ -170,7 +172,13 @@
             mCurrentTestPosition = position;
             ((DialogTestListItem)test).performTest(this);
         } else {
-            super.handleItemClick(l, v, position, id);
+            try {
+                super.handleItemClick(l, v, position, id);
+            } catch (ActivityNotFoundException e) {
+                Log.d(TAG, "handleItemClick() threw exception: ", e);
+                setTestResult(test, TestResult.TEST_RESULT_FAILED);
+                showToast(R.string.test_failed_cannot_start_intent);
+            }
         }
     }
 
@@ -196,7 +204,7 @@
         // do nothing, override in subclass if needed
     }
 
-    protected void setTestResult(DialogTestListItem test, int result) {
+    protected void setTestResult(TestListAdapter.TestListItem test, int result) {
         // Bundle result in an intent to feed into handleLaunchTestResult
         Intent resultIntent = new Intent();
         TestResult.addResultData(resultIntent, result, test.testName, /* testDetails */ null,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
index d3e2571..508fae0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
@@ -64,6 +64,9 @@
     OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
     Context mContext;
 
+    Button mHeadsetPortYes;
+    Button mHeadsetPortNo;
+
     Button mLoopbackPlugReady;
     LinearLayout mLinearLayout;
     Button mTestButton;
@@ -116,6 +119,20 @@
                     Log.i(TAG, "audio loopback test");
                     startAudioTest();
                     break;
+                case R.id.audio_general_headset_yes:
+                    Log.i(TAG, "User confirms Headset Port existence");
+                    mLoopbackPlugReady.setEnabled(true);
+                    recordHeasetPortFound(true);
+                    mHeadsetPortYes.setEnabled(false);
+                    mHeadsetPortNo.setEnabled(false);
+                    break;
+                case R.id.audio_general_headset_no:
+                    Log.i(TAG, "User denies Headset Port existence");
+                    recordHeasetPortFound(false);
+                    getPassButton().setEnabled(true);
+                    mHeadsetPortYes.setEnabled(false);
+                    mHeadsetPortNo.setEnabled(false);
+                    break;
             }
         }
     }
@@ -127,8 +144,14 @@
 
         mContext = this;
 
+        mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+        mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+        mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+        mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
         mLoopbackPlugReady = (Button)findViewById(R.id.audio_frequency_line_plug_ready_btn);
         mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
+        mLoopbackPlugReady.setEnabled(false);
         mLinearLayout = (LinearLayout)findViewById(R.id.audio_frequency_line_layout);
         mTestButton = (Button)findViewById(R.id.audio_frequency_line_test_btn);
         mTestButton.setOnClickListener(mBtnClickListener);
@@ -479,6 +502,14 @@
         Log.v(TAG, "Results Recorded");
     }
 
+    private void recordHeasetPortFound(boolean found) {
+        getReportLog().addValue(
+                "User Reported Headset Port",
+                found ? 1.0 : 0,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+    }
+
     private void startRecording() {
         synchronized (mRecordingLock) {
             mIsRecording = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
index b37a721..03d84e1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
@@ -72,6 +72,9 @@
     final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
     Context mContext;
 
+    Button mHeadsetPortYes;
+    Button mHeadsetPortNo;
+
     Button mSpeakersReady;              //user signal to have connected external speakers
     Button mTest1Button;                //execute test 1
     Button mUsbMicReady;          //user signal to have connected USB Microphone
@@ -137,6 +140,20 @@
             case R.id.audio_frequency_mic_test2_btn:
                 startTest2();
                 break;
+            case R.id.audio_general_headset_yes:
+                Log.i(TAG, "User confirms Headset Port existence");
+                mSpeakersReady.setEnabled(true);
+                recordHeasetPortFound(true);
+                mHeadsetPortYes.setEnabled(false);
+                mHeadsetPortNo.setEnabled(false);
+                break;
+            case R.id.audio_general_headset_no:
+                Log.i(TAG, "User denies Headset Port existence");
+                recordHeasetPortFound(false);
+                getPassButton().setEnabled(true);
+                mHeadsetPortYes.setEnabled(false);
+                mHeadsetPortNo.setEnabled(false);
+                break;
             }
         }
     }
@@ -146,10 +163,17 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.audio_frequency_mic_activity);
         mContext = this;
+
+        mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+        mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+        mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+        mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
         mSpeakerReadyText = (TextView) findViewById(R.id.audio_frequency_mic_speakers_ready_status);
 
         mSpeakersReady  = (Button)findViewById(R.id.audio_frequency_mic_speakers_ready_btn);
         mSpeakersReady.setOnClickListener(mBtnClickListener);
+        mSpeakersReady.setEnabled(false);
         mTest1Button = (Button)findViewById(R.id.audio_frequency_mic_test1_btn);
         mTest1Button.setOnClickListener(mBtnClickListener);
         mTest1Result = (TextView)findViewById(R.id.audio_frequency_mic_results1_text);
@@ -644,6 +668,14 @@
         Log.v(TAG, "Results Recorded");
     }
 
+    private void recordHeasetPortFound(boolean found) {
+        getReportLog().addValue(
+                "User Reported Headset Port",
+                found ? 1.0 : 0,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+    }
+
     private void startRecording() {
         synchronized (mRecordingLock) {
             mIsRecording = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
similarity index 86%
copy from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
index 93e0507..3513774 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
@@ -34,10 +34,10 @@
 import android.widget.TextView;
 
 /**
- * Tests Audio Device Connection events by prompting the user to insert/remove a wired headset
- * and noting the presence (or absence) of notifictions.
+ * Tests Audio Device Connection events for output by prompting the user to insert/remove a
+ * wired headset (or microphone) and noting the presence (or absence) of notifications.
  */
-public class AudioDeviceNotificationsActivity extends PassFailButtons.Activity {
+public class AudioInputDeviceNotificationsActivity extends PassFailButtons.Activity {
     Context mContext;
 
     TextView mConnectView;
@@ -71,6 +71,9 @@
         mConnectView = (TextView)findViewById(R.id.audio_dev_notification_connect_msg);
         mDisconnectView = (TextView)findViewById(R.id.audio_dev_notification_disconnect_msg);
 
+        ((TextView)findViewById(R.id.info_text)).setText(mContext.getResources().getString(
+                R.string.audio_in_devices_notification_instructions));
+
         mClearMsgsBtn = (Button)findViewById(R.id.audio_dev_notification_connect_clearmsgs_btn);
         mClearMsgsBtn.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
similarity index 61%
copy from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
index b6a4255..cdc8199 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
@@ -25,7 +25,6 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.AudioRecord;
-import android.media.AudioTrack;
 
 import android.os.Bundle;
 import android.os.Handler;
@@ -39,60 +38,34 @@
 import android.widget.TextView;
 
 /**
- * Tests AudioTrack and AudioRecord (re)Routing messages.
+ * Tests AudioRecord (re)Routing messages.
  */
-public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
-    private static final String TAG = "AudioRoutingNotificationsActivity";
+public class AudioInputRoutingNotificationsActivity extends PassFailButtons.Activity {
+    private static final String TAG = "AudioInputRoutingNotificationsActivity";
 
     Context mContext;
 
-    OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
-
-    int mNumTrackNotifications = 0;
     int mNumRecordNotifications = 0;
 
-    TrivialPlayer mAudioPlayer = new TrivialPlayer();
+    OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
     TrivialRecorder mAudioRecorder = new TrivialRecorder();
 
     private class OnBtnClickListener implements OnClickListener {
         @Override
         public void onClick(View v) {
             switch (v.getId()) {
-                case R.id.audio_routingnotification_playBtn:
-                    Log.i(TAG, "audio_routingnotification_playBtn");
-                    mAudioPlayer.start();
-                    break;
-
-                case R.id.audio_routingnotification_playStopBtn:
-                    Log.i(TAG, "audio_routingnotification_playStopBtn");
-                    mAudioPlayer.stop();
-                    break;
-
                 case R.id.audio_routingnotification_recordBtn:
+                    mAudioRecorder.start();
                     break;
 
                 case R.id.audio_routingnotification_recordStopBtn:
+                    mAudioRecorder.stop();
                     break;
             }
         }
     }
 
-    private class AudioTrackRoutingChangeListener implements AudioTrack.OnRoutingChangedListener {
-        public void onRoutingChanged(AudioTrack audioTrack) {
-            mNumTrackNotifications++;
-            TextView textView =
-                (TextView)findViewById(R.id.audio_routingnotification_audioTrack_change);
-            String msg = mContext.getResources().getString(
-                    R.string.audio_routingnotification_trackRoutingMsg);
-            AudioDeviceInfo routedDevice = audioTrack.getRoutedDevice();
-            CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
-            int deviceType = routedDevice != null ? routedDevice.getType() : -1;
-            textView.setText(msg + " - " +
-                             deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
-                             " - " + mNumTrackNotifications);
-        }
-    }
-
     private class AudioRecordRoutingChangeListener implements AudioRecord.OnRoutingChangedListener {
         public void onRoutingChanged(AudioRecord audioRecord) {
             mNumRecordNotifications++;
@@ -112,13 +85,9 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.audio_routingnotifications_test);
+        setContentView(R.layout.audio_input_routingnotifications_test);
 
         Button btn;
-        btn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
-        btn.setOnClickListener(mBtnClickListener);
-        btn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
-        btn.setOnClickListener(mBtnClickListener);
         btn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
         btn.setOnClickListener(mBtnClickListener);
         btn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
@@ -126,10 +95,6 @@
 
         mContext = this;
 
-        AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
-        audioTrack.addOnRoutingChangedListener(
-            new AudioTrackRoutingChangeListener(), new Handler());
-
         AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
         audioRecord.addOnRoutingChangedListener(
             new AudioRecordRoutingChangeListener(), new Handler());
@@ -139,7 +104,6 @@
 
     @Override
     public void onBackPressed () {
-        mAudioPlayer.shutDown();
         mAudioRecorder.shutDown();
         super.onBackPressed();
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
index e603a69..fbec57a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
@@ -61,6 +61,9 @@
     OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
     Context mContext;
 
+    Button mHeadsetPortYes;
+    Button mHeadsetPortNo;
+
     Button mLoopbackPlugReady;
     TextView mAudioLevelText;
     SeekBar mAudioLevelSeekbar;
@@ -83,7 +86,20 @@
                     Log.i(TAG, "audio loopback test");
                     startAudioTest();
                     break;
-
+                case R.id.audio_general_headset_yes:
+                    Log.i(TAG, "User confirms Headset Port existence");
+                    mLoopbackPlugReady.setEnabled(true);
+                    recordHeasetPortFound(true);
+                    mHeadsetPortYes.setEnabled(false);
+                    mHeadsetPortNo.setEnabled(false);
+                    break;
+                case R.id.audio_general_headset_no:
+                    Log.i(TAG, "User denies Headset Port existence");
+                    recordHeasetPortFound(false);
+                    getPassButton().setEnabled(true);
+                    mHeadsetPortYes.setEnabled(false);
+                    mHeadsetPortNo.setEnabled(false);
+                    break;
             }
         }
     }
@@ -95,8 +111,14 @@
 
         mContext = this;
 
+        mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+        mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+        mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+        mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
         mLoopbackPlugReady = (Button)findViewById(R.id.audio_loopback_plug_ready_btn);
         mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
+        mLoopbackPlugReady.setEnabled(false);
         mLinearLayout = (LinearLayout)findViewById(R.id.audio_loopback_layout);
         mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
         mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
@@ -135,7 +157,7 @@
 
         setPassFailButtonClickListeners();
         getPassButton().setEnabled(false);
-        setInfoResources(R.string.sample_test, R.string.audio_loopback_info, -1);
+        setInfoResources(R.string.audio_loopback_test, R.string.audio_loopback_info, -1);
     }
 
     /**
@@ -304,4 +326,12 @@
 
         Log.v(TAG,"Results Recorded");
     }
+
+    private void recordHeasetPortFound(boolean found) {
+        getReportLog().addValue(
+                "User Reported Headset Port",
+                found ? 1.0 : 0,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
similarity index 86%
rename from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
index 93e0507..a64ddc4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
@@ -34,10 +34,10 @@
 import android.widget.TextView;
 
 /**
- * Tests Audio Device Connection events by prompting the user to insert/remove a wired headset
- * and noting the presence (or absence) of notifictions.
+ * Tests Audio Device Connection events for output devices by prompting the user to
+ * insert/remove a wired headset and noting the presence (or absence) of notifications.
  */
-public class AudioDeviceNotificationsActivity extends PassFailButtons.Activity {
+public class AudioOutputDeviceNotificationsActivity extends PassFailButtons.Activity {
     Context mContext;
 
     TextView mConnectView;
@@ -71,6 +71,9 @@
         mConnectView = (TextView)findViewById(R.id.audio_dev_notification_connect_msg);
         mDisconnectView = (TextView)findViewById(R.id.audio_dev_notification_disconnect_msg);
 
+        ((TextView)findViewById(R.id.info_text)).setText(mContext.getResources().getString(
+                R.string.audio_out_devices_notification_instructions));
+
         mClearMsgsBtn = (Button)findViewById(R.id.audio_dev_notification_connect_clearmsgs_btn);
         mClearMsgsBtn.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
similarity index 62%
rename from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
index b6a4255..bfc3d45 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
@@ -24,7 +24,6 @@
 import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
-import android.media.AudioRecord;
 import android.media.AudioTrack;
 
 import android.os.Bundle;
@@ -41,38 +40,28 @@
 /**
  * Tests AudioTrack and AudioRecord (re)Routing messages.
  */
-public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
-    private static final String TAG = "AudioRoutingNotificationsActivity";
+public class AudioOutputRoutingNotificationsActivity extends PassFailButtons.Activity {
+    private static final String TAG = "AudioOutputRoutingNotificationsActivity";
 
     Context mContext;
 
     OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
 
     int mNumTrackNotifications = 0;
-    int mNumRecordNotifications = 0;
 
     TrivialPlayer mAudioPlayer = new TrivialPlayer();
-    TrivialRecorder mAudioRecorder = new TrivialRecorder();
 
     private class OnBtnClickListener implements OnClickListener {
         @Override
         public void onClick(View v) {
             switch (v.getId()) {
                 case R.id.audio_routingnotification_playBtn:
-                    Log.i(TAG, "audio_routingnotification_playBtn");
                     mAudioPlayer.start();
                     break;
 
                 case R.id.audio_routingnotification_playStopBtn:
-                    Log.i(TAG, "audio_routingnotification_playStopBtn");
                     mAudioPlayer.stop();
                     break;
-
-                case R.id.audio_routingnotification_recordBtn:
-                    break;
-
-                case R.id.audio_routingnotification_recordStopBtn:
-                    break;
             }
         }
     }
@@ -93,36 +82,16 @@
         }
     }
 
-    private class AudioRecordRoutingChangeListener implements AudioRecord.OnRoutingChangedListener {
-        public void onRoutingChanged(AudioRecord audioRecord) {
-            mNumRecordNotifications++;
-            TextView textView =
-                    (TextView)findViewById(R.id.audio_routingnotification_audioRecord_change);
-            String msg = mContext.getResources().getString(
-                    R.string.audio_routingnotification_recordRoutingMsg);
-            AudioDeviceInfo routedDevice = audioRecord.getRoutedDevice();
-            CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
-            int deviceType = routedDevice != null ? routedDevice.getType() : -1;
-            textView.setText(msg + " - " +
-                             deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
-                             " - " + mNumRecordNotifications);
-        }
-    }
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.audio_routingnotifications_test);
+        setContentView(R.layout.audio_output_routingnotifications_test);
 
         Button btn;
         btn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
         btn.setOnClickListener(mBtnClickListener);
         btn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
         btn.setOnClickListener(mBtnClickListener);
-        btn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
-        btn.setOnClickListener(mBtnClickListener);
-        btn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
-        btn.setOnClickListener(mBtnClickListener);
 
         mContext = this;
 
@@ -130,17 +99,12 @@
         audioTrack.addOnRoutingChangedListener(
             new AudioTrackRoutingChangeListener(), new Handler());
 
-        AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
-        audioRecord.addOnRoutingChangedListener(
-            new AudioRecordRoutingChangeListener(), new Handler());
-
         setPassFailButtonClickListeners();
     }
 
     @Override
     public void onBackPressed () {
         mAudioPlayer.shutDown();
-        mAudioRecorder.shutDown();
         super.onBackPressed();
     }
 }
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 27f8c28..e3ff74b 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
@@ -39,12 +39,14 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.ImageWriter;
+import android.media.Image.Plane;
 import android.net.Uri;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.SystemClock;
 import android.os.Vibrator;
 import android.util.Log;
 import android.util.Rational;
@@ -56,6 +58,8 @@
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 
+import com.android.cts.verifier.camera.its.StatsImage;
+
 import org.json.JSONArray;
 import org.json.JSONObject;
 
@@ -70,6 +74,8 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
 import java.nio.charset.Charset;
 import java.security.MessageDigest;
 import java.util.ArrayList;
@@ -154,6 +160,9 @@
     private AtomicInteger mCountYuv = new AtomicInteger();
     private AtomicInteger mCountCapRes = new AtomicInteger();
     private boolean mCaptureRawIsDng;
+    private boolean mCaptureRawIsStats;
+    private int mCaptureStatsGridWidth;
+    private int mCaptureStatsGridHeight;
     private CaptureResult mCaptureResults[] = null;
 
     private volatile ConditionVariable mInterlock3A = new ConditionVariable(true);
@@ -404,7 +413,7 @@
                             continue;
                         }
                         if (b.hasArray()) {
-                            mOpenSocket.getOutputStream().write(b.array());
+                            mOpenSocket.getOutputStream().write(b.array(), 0, b.capacity());
                         } else {
                             byte[] barray = new byte[b.capacity()];
                             b.get(barray);
@@ -665,7 +674,16 @@
                     jsonSurface.put("height", readers[i].getHeight());
                     int format = readers[i].getImageFormat();
                     if (format == ImageFormat.RAW_SENSOR) {
-                        jsonSurface.put("format", "raw");
+                        if (mCaptureRawIsStats) {
+                            jsonSurface.put("format", "rawStats");
+                            jsonSurface.put("width", readers[i].getWidth()/mCaptureStatsGridWidth);
+                            jsonSurface.put("height",
+                                    readers[i].getHeight()/mCaptureStatsGridHeight);
+                        } else if (mCaptureRawIsDng) {
+                            jsonSurface.put("format", "dng");
+                        } else {
+                            jsonSurface.put("format", "raw");
+                        }
                     } else if (format == ImageFormat.RAW10) {
                         jsonSurface.put("format", "raw10");
                     } else if (format == ImageFormat.RAW12) {
@@ -1068,6 +1086,12 @@
                         outputFormats[i] = ImageFormat.RAW_SENSOR;
                         sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
                         mCaptureRawIsDng = true;
+                    } else if ("rawStats".equals(sformat)) {
+                        outputFormats[i] = ImageFormat.RAW_SENSOR;
+                        sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
+                        mCaptureRawIsStats = true;
+                        mCaptureStatsGridWidth = surfaceObj.optInt("gridWidth");
+                        mCaptureStatsGridHeight = surfaceObj.optInt("gridHeight");
                     } else {
                         throw new ItsException("Unsupported format: " + sformat);
                     }
@@ -1086,6 +1110,14 @@
                     if (height <= 0) {
                         height = ItsUtils.getMaxSize(sizes).getHeight();
                     }
+                    if (mCaptureStatsGridWidth <= 0) {
+                        mCaptureStatsGridWidth = width;
+                    }
+                    if (mCaptureStatsGridHeight <= 0) {
+                        mCaptureStatsGridHeight = height;
+                    }
+
+                    // TODO: Crop to the active array in the stats image analysis.
 
                     outputSizes[i] = new Size(width, height);
                 }
@@ -1122,6 +1154,7 @@
                 mCountRaw12.set(0);
                 mCountCapRes.set(0);
                 mCaptureRawIsDng = false;
+                mCaptureRawIsStats = false;
                 mCaptureResults = new CaptureResult[requests.size()];
 
                 JSONArray jsonOutputSpecs = ItsUtils.getOutputSpecs(params);
@@ -1235,6 +1268,7 @@
         mCountRaw12.set(0);
         mCountCapRes.set(0);
         mCaptureRawIsDng = false;
+        mCaptureRawIsStats = false;
 
         try {
             // Parse the JSON to get the list of capture requests.
@@ -1427,8 +1461,28 @@
                     int count = mCountRawOrDng.getAndIncrement();
                     if (! mCaptureRawIsDng) {
                         byte[] img = ItsUtils.getDataFromImage(capture);
-                        ByteBuffer buf = ByteBuffer.wrap(img);
-                        mSocketRunnableObj.sendResponseCaptureBuffer("rawImage", buf);
+                        if (! mCaptureRawIsStats) {
+                            ByteBuffer buf = ByteBuffer.wrap(img);
+                            mSocketRunnableObj.sendResponseCaptureBuffer("rawImage", buf);
+                        } else {
+                            // Compute the requested stats on the raw frame, and return the results
+                            // in a new "stats image".
+                            long startTimeMs = SystemClock.elapsedRealtime();
+                            int w = capture.getWidth();
+                            int h = capture.getHeight();
+                            int gw = mCaptureStatsGridWidth;
+                            int gh = mCaptureStatsGridHeight;
+                            float[] stats = StatsImage.computeStatsImage(img, w, h, gw, gh);
+                            long endTimeMs = SystemClock.elapsedRealtime();
+                            Log.e(TAG, "Raw stats computation takes " + (endTimeMs - startTimeMs) + " ms");
+
+                            ByteBuffer bBuf = ByteBuffer.allocateDirect(stats.length * 4);
+                            bBuf.order(ByteOrder.nativeOrder());
+                            FloatBuffer fBuf = bBuf.asFloatBuffer();
+                            fBuf.put(stats);
+                            fBuf.position(0);
+                            mSocketRunnableObj.sendResponseCaptureBuffer("rawStatsImage", bBuf);
+                        }
                     } else {
                         // Wait until the corresponding capture result is ready, up to a timeout.
                         long t0 = android.os.SystemClock.elapsedRealtime();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/StatsImage.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/StatsImage.java
new file mode 100644
index 0000000..037177c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/StatsImage.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.camera.its;
+
+import android.util.Log;
+
+public class StatsImage {
+
+    static {
+        try {
+            System.loadLibrary("ctsverifier_jni");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e("StatsImage", "Error loading cts verifier JNI library");
+            e.printStackTrace();
+        }
+    }
+
+    public native static float[] computeStatsImage(
+            byte[] img, int width, int height, int gridW, int gridH);
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
new file mode 100644
index 0000000..073412d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
@@ -0,0 +1,381 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.KeyguardManager;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.CountDownTimer;
+import android.provider.Settings;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.UserNotAuthenticatedException;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.DialogTestListActivity;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Test device credential-bound keys in work profile.
+ * Currently there are two types, one is keys bound to lockscreen passwords which can be configured
+ * to remain available within a certain timeout after the latest successful user authentication.
+ * The other is keys bound to fingerprint authentication which require explicit fingerprint
+ * authentication before they can be accessed.
+ */
+public class AuthenticationBoundKeyTestActivity extends DialogTestListActivity {
+
+    public static final String ACTION_AUTH_BOUND_KEY_TEST =
+            "com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST";
+
+    private static final int AUTHENTICATION_DURATION_SECONDS = 5;
+    private static final String LOCKSCREEN_KEY_NAME = "mp_lockscreen_key";
+    private static final String FINGERPRINT_KEY_NAME = "mp_fingerprint_key";
+    private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
+    private static final int CONFIRM_CREDENTIALS_REQUEST_CODE = 1;
+    private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
+
+    private static final int LOCKSCREEN = 1;
+    private static final int FINGERPRINT = 2;
+
+    private static final String KEYSTORE_NAME = "AndroidKeyStore";
+    private static final String CIPHER_TRANSFORMATION =  KeyProperties.KEY_ALGORITHM_AES + "/"
+            + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7;
+
+
+    private KeyguardManager mKeyguardManager;
+    private FingerprintManager mFingerprintManager;
+    private boolean mFingerprintSupported;
+
+    private DialogTestListItem mLockScreenBoundKeyTest;
+    private DialogTestListItem mFingerprintBoundKeyTest;
+
+    private Cipher mFingerprintCipher;
+
+    public AuthenticationBoundKeyTestActivity() {
+        super(R.layout.provisioning_byod,
+                R.string.provisioning_byod_auth_bound_key,
+                R.string.provisioning_byod_auth_bound_key_info,
+                R.string.provisioning_byod_auth_bound_key_instruction);
+    }
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+        mFingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
+        mFingerprintSupported = mFingerprintManager != null
+                && mFingerprintManager.isHardwareDetected();
+        // Need to have valid mFingerprintSupported value before calling super.onCreate() because
+        // mFingerprintSupported is used in setupTests() which gets called by super.onCreate().
+        super.onCreate(savedInstanceState);
+
+        mPrepareTestButton.setText(R.string.provisioning_byod_auth_bound_key_set_up);
+        mPrepareTestButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View arg0) {
+                startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
+            }
+        });
+        if (mFingerprintSupported) {
+            requestPermissions(new String[] {Manifest.permission.USE_FINGERPRINT},
+                    FINGERPRINT_PERMISSION_REQUEST_CODE);
+        }
+    }
+
+    private class LockscreenCountDownTester extends CountDownTimer {
+
+        private Toast mToast;
+
+        public LockscreenCountDownTester() {
+            // Wait for AUTHENTICATION_DURATION_SECONDS so the key is evicted before the real test.
+            super(AUTHENTICATION_DURATION_SECONDS * 1000, 1000);
+            mToast = Toast.makeText(AuthenticationBoundKeyTestActivity.this, "", Toast.LENGTH_SHORT);
+        }
+
+        @Override
+        public void onFinish() {
+            mToast.cancel();
+            if (tryEncryptWithLockscreenKey()) {
+                showToast("Test failed. Key accessible without auth.");
+                setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+            } else {
+                // Start the Confirm Credentials screen.
+                Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
+                if (intent != null) {
+                    startActivityForResult(intent, CONFIRM_CREDENTIALS_REQUEST_CODE);
+                } else {
+                    showToast("Test failed. No lockscreen password exists.");
+                    setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+                }
+            }
+        }
+
+        @Override
+        public void onTick(long millisUntilFinished) {
+            mToast.setText(String.format("Lockscreen challenge start in %d seconds..",
+                    millisUntilFinished / 1000));
+            mToast.show();
+        }
+    }
+
+
+    @Override
+    protected void setupTests(ArrayTestListAdapter adapter) {
+        mLockScreenBoundKeyTest = new DialogTestListItem(this,
+                R.string.provisioning_byod_lockscreen_bound_key,
+                "BYOD_LockScreenBoundKeyTest") {
+
+            @Override
+            public void performTest(DialogTestListActivity activity) {
+                if (checkPreconditions()) {
+                    createKey(LOCKSCREEN);
+                    new LockscreenCountDownTester().start();
+                }
+            }
+        };
+        adapter.add(mLockScreenBoundKeyTest);
+
+        if (mFingerprintSupported) {
+            mFingerprintBoundKeyTest = new DialogTestListItem(this,
+                    R.string.provisioning_byod_fingerprint_bound_key,
+                    "BYOD_FingerprintBoundKeyTest") {
+
+                @Override
+                public void performTest(DialogTestListActivity activity) {
+                    if (checkPreconditions()) {
+                        createKey(FINGERPRINT);
+                        mFingerprintCipher = initFingerprintEncryptionCipher();
+                        if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+                            showToast("Test failed. Key accessible without auth.");
+                            setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+                        } else {
+                            new FingerprintAuthDialogFragment().show(getFragmentManager(),
+                                    "fingerprint_dialog");
+                        }
+                    }
+                }
+            };
+            adapter.add(mFingerprintBoundKeyTest);
+        }
+    }
+
+    private boolean checkPreconditions() {
+        if (!mKeyguardManager.isKeyguardSecure()) {
+            showToast("Please set a lockscreen password.");
+            return false;
+        } else if (mFingerprintSupported && !mFingerprintManager.hasEnrolledFingerprints()) {
+            showToast("Please enroll a fingerprint.");
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private String getKeyName(int testType) {
+        return testType == LOCKSCREEN ? LOCKSCREEN_KEY_NAME : FINGERPRINT_KEY_NAME;
+    }
+    /**
+     * Creates a symmetric key in the Android Key Store which can only be used after the user has
+     * authenticated with device credentials.
+     */
+    private void createKey(int testType) {
+        try {
+            // Set the alias of the entry in Android KeyStore where the key will appear
+            // and the constrains (purposes) in the constructor of the Builder
+            KeyGenParameterSpec.Builder builder;
+            builder = new KeyGenParameterSpec.Builder(getKeyName(testType),
+                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+                    .setUserAuthenticationRequired(true)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
+            if (testType == LOCKSCREEN) {
+                // Require that the user unlocked the lockscreen in the last 5 seconds
+                builder.setUserAuthenticationValidityDurationSeconds(
+                        AUTHENTICATION_DURATION_SECONDS);
+            }
+            KeyGenerator keyGenerator = KeyGenerator.getInstance(
+                    KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
+            keyGenerator.init(builder.build());
+            keyGenerator.generateKey();
+        } catch (NoSuchAlgorithmException | NoSuchProviderException
+                | InvalidAlgorithmParameterException e) {
+            throw new RuntimeException("Failed to create a symmetric key", e);
+        }
+    }
+
+    private SecretKey loadSecretKey(int testType) {
+        try {
+            KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME);
+            keyStore.load(null);
+            return (SecretKey) keyStore.getKey(getKeyName(testType), null);
+        } catch (UnrecoverableKeyException  | CertificateException |KeyStoreException | IOException
+                | NoSuchAlgorithmException e) {
+            throw new RuntimeException("Failed to load a symmetric key", e);
+        }
+    }
+
+    private boolean tryEncryptWithLockscreenKey() {
+        try {
+            // Try encrypting something, it will only work if the user authenticated within
+            // the last AUTHENTICATION_DURATION_SECONDS seconds.
+            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+            cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(LOCKSCREEN));
+            cipher.doFinal(SECRET_BYTE_ARRAY);
+            return true;
+        } catch (UserNotAuthenticatedException e) {
+            // User is not authenticated, let's authenticate with device credentials.
+            return false;
+        } catch (KeyPermanentlyInvalidatedException e) {
+            // This happens if the lock screen has been disabled or reset after the key was
+            // generated.
+            createKey(LOCKSCREEN);
+            showToast("Set up lockscreen after test ran. Retry the test.");
+            return false;
+        } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException
+                | NoSuchPaddingException | NoSuchAlgorithmException e) {
+            throw new RuntimeException("Encrypt with lockscreen-bound key failed", e);
+        }
+    }
+
+    private Cipher initFingerprintEncryptionCipher() {
+        try {
+            Cipher cipher =  Cipher.getInstance(CIPHER_TRANSFORMATION);
+            cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(FINGERPRINT));
+            return cipher;
+        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
+            return null;
+        } catch (KeyPermanentlyInvalidatedException e) {
+            // This happens if the lock screen has been disabled or reset after the key was
+            // generated after the key was generated.
+            createKey(FINGERPRINT);
+            showToast("Set up lockscreen after test ran. Retry the test.");
+            return null;
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException("Init cipher with fingerprint-bound key failed", e);
+        }
+    }
+
+    private boolean tryEncryptWithFingerprintKey(Cipher cipher) {
+
+        try {
+            cipher.doFinal(SECRET_BYTE_ARRAY);
+            return true;
+        } catch (IllegalBlockSizeException e) {
+            // Cannot encrypt, key is unavailable
+            return false;
+        } catch (BadPaddingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    protected void handleActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case CONFIRM_CREDENTIALS_REQUEST_CODE:
+                if (resultCode == RESULT_OK) {
+                    if (tryEncryptWithLockscreenKey()) {
+                        setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+                    } else {
+                        showToast("Test failed. Key not accessible after auth");
+                        setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+                    }
+                } else {
+                    showToast("Lockscreen challenge canceled.");
+                    setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+                }
+                break;
+            default:
+                super.handleActivityResult(requestCode, resultCode, data);
+        }
+    }
+
+    private void showToast(String message) {
+        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+    }
+
+    public class FingerprintAuthDialogFragment extends DialogFragment {
+
+        private CancellationSignal mCancellationSignal;
+        private FingerprintManagerCallback mFingerprintManagerCallback;
+        private boolean mSelfCancelled;
+
+        class FingerprintManagerCallback extends FingerprintManager.AuthenticationCallback {
+            @Override
+            public void onAuthenticationError(int errMsgId, CharSequence errString) {
+                if (!mSelfCancelled) {
+                    showToast(errString.toString());
+                }
+            }
+
+            @Override
+            public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+                showToast(helpString.toString());
+            }
+
+            @Override
+            public void onAuthenticationFailed() {
+                showToast(getString(R.string.sec_fp_auth_failed));
+            }
+
+            @Override
+            public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+                if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+                    showToast("Test passed.");
+                    setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+                } else {
+                    showToast("Test failed. Key not accessible after auth");
+                    setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+                }
+                FingerprintAuthDialogFragment.this.dismiss();
+            }
+        }
+
+        @Override
+        public void onDismiss(DialogInterface dialog) {
+            mCancellationSignal.cancel();
+            mSelfCancelled = true;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            mCancellationSignal = new CancellationSignal();
+            mSelfCancelled = false;
+            mFingerprintManagerCallback = new FingerprintManagerCallback();
+            mFingerprintManager.authenticate(new FingerprintManager.CryptoObject(mFingerprintCipher),
+                    mCancellationSignal, 0, mFingerprintManagerCallback, null);
+            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+            builder.setMessage(R.string.sec_fp_dialog_message);
+            return builder.create();
+        }
+
+    }
+
+}
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 91f0f91..b129665 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
@@ -80,6 +81,7 @@
     private DialogTestListItem mCrossProfileAudioCaptureSupportTest;
     private TestListItem mKeyguardDisabledFeaturesTest;
     private DialogTestListItem mDisableNfcBeamTest;
+    private TestListItem mAuthenticationBoundKeyTest;
 
     public ByodFlowTestActivity() {
         super(R.layout.provisioning_byod,
@@ -293,6 +295,12 @@
                 KeyguardDisabledFeaturesActivity.class.getName(),
                 new Intent(this, KeyguardDisabledFeaturesActivity.class), null);
 
+        mAuthenticationBoundKeyTest = TestListItem.newTest(this,
+                R.string.provisioning_byod_auth_bound_key,
+                AuthenticationBoundKeyTestActivity.class.getName(),
+                new Intent(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST),
+                null);
+
         // Test for checking if the required intent filters are set during managed provisioning.
         mIntentFiltersTest = new DialogTestListItem(this,
                 R.string.provisioning_byod_cross_profile_intent_filters,
@@ -302,7 +310,7 @@
                 checkIntentFilters();
             }
         };
-        
+
         Intent permissionCheckIntent = new Intent(
                 PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
         mPermissionLockdownTest = new DialogTestListItem(this,
@@ -337,6 +345,7 @@
         adapter.add(mIntentFiltersTest);
         adapter.add(mPermissionLockdownTest);
         adapter.add(mKeyguardDisabledFeaturesTest);
+        adapter.add(mAuthenticationBoundKeyTest);
 
         if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
             // Capture image intent can be resolved in primary profile, so test.
@@ -512,7 +521,8 @@
             ByodHelperActivity.class.getName(),
             WorkNotificationTestActivity.class.getName(),
             WorkStatusTestActivity.class.getName(),
-            PermissionLockdownTestActivity.ACTIVITY_ALIAS
+            PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+            AuthenticationBoundKeyTestActivity.class.getName()
         };
         for (String component : components) {
             getPackageManager().setComponentEnabledSetting(new ComponentName(this, component),
@@ -520,4 +530,5 @@
                     PackageManager.DONT_KILL_APP);
         }
     }
+
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
index b3f126b..15f5bc8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
@@ -31,10 +31,14 @@
 import android.widget.ImageView;
 import android.widget.Toast;
 import android.widget.VideoView;
-
+import android.view.Display;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.content.ContentResolver;
 import com.android.cts.verifier.R;
 
 import java.io.IOException;
+import java.io.InputStream;
 
 /**
  * This dialog shows/plays an image, video or audio uri.
@@ -46,6 +50,7 @@
     private static final String KEY_IMAGE_URI = "image";
     private static final String KEY_AUDIO_URI = "audio";
 
+    private Bitmap scaled = null;
     /**
      * Get a dialogFragment showing an image.
      */
@@ -79,6 +84,16 @@
         return dialog;
     }
 
+    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
+        // Raw height and width of image
+        final int height = options.outHeight;
+        final int width = options.outWidth;
+        if(reqWidth <= 0 || reqHeight <= 0) {
+           return 1;
+        }
+        return Math.max(height/reqHeight, width/reqWidth) + 1;
+    }
+
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         final Dialog dialog = new Dialog(getActivity());
@@ -114,11 +129,36 @@
         } 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);
+            Uri uri = (Uri)getArguments().getParcelable(KEY_IMAGE_URI);
             ImageView imageView = (ImageView) dialog.findViewById(R.id.imageView);
             imageView.setVisibility(View.VISIBLE);
-            imageView.setImageURI(uri);
+
+            try{
+                InputStream input = getActivity().getContentResolver().openInputStream(uri);
+                BitmapFactory.Options options = new BitmapFactory.Options();
+                options.inJustDecodeBounds = true;
+                BitmapFactory.decodeStream(input, null, options);
+                //scale the picture
+                Display display = getActivity().getWindowManager().getDefaultDisplay();
+                Point size = new Point();
+                display.getSize(size);
+                int reqWidth = size.x;
+                int reqHeight = size.y;
+                options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+                options.inJustDecodeBounds = false;
+                input.close();
+                input = getActivity().getContentResolver().openInputStream(uri);
+                scaled = BitmapFactory.decodeStream(input, null, options);
+                input.close();
+                imageView.setImageBitmap(scaled);
+            }catch(IOException e){
+                Log.e(TAG, "Cannot get image.", e);
+                Toast.makeText(getActivity(),R.string.provisioning_byod_capture_image_error,
+                        Toast.LENGTH_SHORT).show();
+                getActivity().finish();
+            }
+
         } else if (arguments.containsKey(KEY_AUDIO_URI)) {
             // Show audio playback UI.
             dialog.setTitle(getString(R.string.provisioning_byod_verify_audio_title));
@@ -161,6 +201,13 @@
         ((DialogCallback) getActivity()).onDialogClose();
     }
 
+    @Override
+    public void onDestroyView() {
+        if(scaled!=null){
+            scaled.recycle();
+        }
+        super.onDestroyView();
+    }
     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 5f870b0..ed6b5eb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -74,6 +74,7 @@
             filter.addAction(WorkStatusTestActivity.ACTION_WORK_STATUS_ICON);
             filter.addAction(
                     PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
+            filter.addAction(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST);
             dpm.addCrossProfileIntentFilter(getWho(context), filter,
                     DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
index 7dc99c0..eef6299 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
@@ -30,6 +30,7 @@
 import android.net.Uri;
 import android.nfc.cardemulation.CardEmulation;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.UserHandle;
 import android.provider.AlarmClock;
 import android.provider.CalendarContract.Events;
@@ -39,6 +40,7 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -51,132 +53,83 @@
     private static final String TAG = "IntentFiltersTestHelper";
 
     // These are the intents which can be forwarded to the managed profile.
-    private static final Intent[] forwardedIntentsFromPrimary = new Intent[] {
-        new Intent(Intent.ACTION_SEND).setType("*/*"),
-        new Intent(Intent.ACTION_SEND_MULTIPLE).setType("*/*")
-    };
+    private static final ArrayList<Intent> forwardedIntentsFromPrimary =
+            new ArrayList<>(Arrays.asList(
+                new Intent(Intent.ACTION_SEND).setType("*/*"),
+                new Intent(Intent.ACTION_SEND_MULTIPLE).setType("*/*")
+            ));
 
     // These are the intents which can be forwarded to the primary profile.
-    private static final Intent[] forwardedIntentsFromManaged = new Intent[] {
-        new Intent(AlarmClock.ACTION_SET_ALARM),
-        new Intent(AlarmClock.ACTION_SET_TIMER),
-        new Intent(AlarmClock.ACTION_SHOW_ALARMS),
-        new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
-        new Intent(MediaStore.ACTION_VIDEO_CAPTURE),
-        new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),
-        new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA),
-        new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE),
-        new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
-        new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("sms:07700900100")),
-        new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("smsto:07700900100")),
-        new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mms:07700900100")),
-        new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mmsto:07700900100")),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("sms:07700900100?body=Hello%20world")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("smsto:07700900100?body=Hello%20world")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("mms:07700900100?body=Hello%20world")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("mmsto:07700900100?body=Hello%20world")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
-        new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS),
-        new Intent(Settings.ACTION_APN_SETTINGS),
-        new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS),
-        new Intent(Settings.ACTION_CAPTIONING_SETTINGS),
-        new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS),
-        new Intent(Settings.ACTION_DATE_SETTINGS),
-        new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS),
-        new Intent(Settings.ACTION_DISPLAY_SETTINGS),
-        new Intent(Settings.ACTION_DREAM_SETTINGS),
-        new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
-        new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS),
-        new Intent(Settings.ACTION_LOCALE_SETTINGS),
-        new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS),
-        new Intent(Settings.ACTION_NFC_SETTINGS),
-        new Intent(Settings.ACTION_NFCSHARING_SETTINGS),
-        new Intent(Settings.ACTION_PRIVACY_SETTINGS),
-        new Intent(Settings.ACTION_SETTINGS),
-        new Intent(Settings.ACTION_SOUND_SETTINGS),
-        new Intent(Settings.ACTION_WIRELESS_SETTINGS),
-        new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD),
-        new Intent("android.net.vpn.SETTINGS"),
-        new Intent(CardEmulation.ACTION_CHANGE_DEFAULT),
-        new Intent("android.settings.ACCOUNT_SYNC_SETTINGS"),
-        new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
-        new Intent(Settings.ACTION_HOME_SETTINGS),
-        new Intent("android.settings.LICENSE"),
-        new Intent("android.settings.NOTIFICATION_SETTINGS"),
-        new Intent(Settings.ACTION_SHOW_REGULATORY_INFO),
-        new Intent("android.settings.USER_SETTINGS"),
-        new Intent("android.settings.ZEN_MODE_SETTINGS"),
-        new Intent("com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS"),
-        new Intent("com.android.settings.STORAGE_USB_SETTINGS"),
-        new Intent("com.android.settings.TTS_SETTINGS"),
-        new Intent("com.android.settings.USER_DICTIONARY_EDIT"),
-        new Intent(Intent.ACTION_CALL).setData(Uri.parse("tel:123")),
-        new Intent("android.intent.action.CALL_EMERGENCY").setData(Uri.parse("tel:123")),
-        new Intent("android.intent.action.CALL_PRIVILEGED").setData(Uri.parse("tel:123")),
-        new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:123")),
-        new Intent(Intent.ACTION_VIEW).setData(Uri.parse("tel:123")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS),
-        new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS),
-        new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
-        new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
-        new Intent(Settings.ACTION_SYNC_SETTINGS),
-        new Intent(Settings.ACTION_ADD_ACCOUNT),
-        new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
-                Intent.CATEGORY_OPENABLE),
-        new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
-                Intent.CATEGORY_OPENABLE),
-        new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS),
-        new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS),
-        new Intent(Settings.ACTION_APPLICATION_SETTINGS),
-        new Intent("android.settings.ACTION_OTHER_SOUND_SETTINGS"),
-        new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION),
-        new Intent(Settings.ACTION_WIFI_IP_SETTINGS),
-        new Intent(Settings.ACTION_WIFI_SETTINGS),
-        new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
-    };
+    private static final ArrayList<Intent> forwardedIntentsFromManaged =
+            new ArrayList<>(Arrays.asList(
+                new Intent(AlarmClock.ACTION_SET_ALARM),
+                new Intent(AlarmClock.ACTION_SET_TIMER),
+                new Intent(AlarmClock.ACTION_SHOW_ALARMS),
+                new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
+                new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS),
+                new Intent(Settings.ACTION_CAPTIONING_SETTINGS),
+                new Intent(Settings.ACTION_DATE_SETTINGS),
+                new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS),
+                new Intent(Settings.ACTION_DISPLAY_SETTINGS),
+                new Intent(Settings.ACTION_LOCALE_SETTINGS),
+                new Intent(Settings.ACTION_PRIVACY_SETTINGS),
+                new Intent(Settings.ACTION_SETTINGS),
+                new Intent(Settings.ACTION_WIRELESS_SETTINGS),
+                new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD),
+                new Intent("android.net.vpn.SETTINGS"),
+                new Intent("android.settings.ACCOUNT_SYNC_SETTINGS"),
+                new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
+                new Intent("android.settings.LICENSE"),
+                new Intent("android.settings.NOTIFICATION_SETTINGS"),
+                new Intent("android.settings.USER_SETTINGS"),
+                new Intent("android.settings.ZEN_MODE_SETTINGS"),
+                new Intent("com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS"),
+                new Intent("com.android.settings.STORAGE_USB_SETTINGS"),
+                new Intent("com.android.settings.TTS_SETTINGS"),
+                new Intent("com.android.settings.USER_DICTIONARY_EDIT"),
+                new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
+                new Intent(Settings.ACTION_SYNC_SETTINGS),
+                new Intent(Settings.ACTION_ADD_ACCOUNT),
+                new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
+                        Intent.CATEGORY_OPENABLE),
+                new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
+                        Intent.CATEGORY_OPENABLE),
+                new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS),
+                new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS),
+                new Intent(Settings.ACTION_APPLICATION_SETTINGS)
+            ));
 
     // These are the intents which cannot be forwarded to the primary profile.
-    private static final Intent[] notForwardedIntentsFromManaged = new Intent[] {
-        new Intent(Intent.ACTION_INSERT).setData(
-                Uri.parse("content://browser/bookmarks")),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("http://www.example.com")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Intent.ACTION_SENDTO).setData(
-                Uri.parse("mailto:user@example.com")),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("mailto:user@example.com")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("geo:0,0?q=BuckinghamPalace")),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("http://example.com/oceans.mp4")).setType("video/mp4"),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("http://www.example.com/horse.mp3")).setType("audio/*"),
-        new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH),
-        new Intent(Intent.ACTION_VIEW).setData(
-                Uri.parse("market://details?id=com.android.chrome")).addCategory(
-                Intent.CATEGORY_BROWSABLE),
-        new Intent(Intent.ACTION_WEB_SEARCH),
-        new Intent(Settings.ACTION_SEARCH_SETTINGS),
-        new Intent(Settings.ACTION_PRINT_SETTINGS),
-        new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE),
-        new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
-                Uri.parse("package:com.android.chrome")),
-        new Intent("android.settings.SHOW_INPUT_METHOD_PICKER"),
-        new Intent(Intent.ACTION_INSERT).setData(Events.CONTENT_URI),
-        new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL),
-        new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
-    };
+    private static final ArrayList<Intent> notForwardedIntentsFromManaged =
+            new ArrayList<>(Arrays.asList(
+                new Intent(Intent.ACTION_INSERT).setData(
+                        Uri.parse("content://browser/bookmarks")),
+                new Intent(Intent.ACTION_VIEW).setData(
+                        Uri.parse("http://www.example.com")).addCategory(
+                        Intent.CATEGORY_BROWSABLE),
+                new Intent(Intent.ACTION_SENDTO).setData(
+                        Uri.parse("mailto:user@example.com")),
+                new Intent(Intent.ACTION_VIEW).setData(
+                        Uri.parse("mailto:user@example.com")).addCategory(
+                        Intent.CATEGORY_BROWSABLE),
+                new Intent(Intent.ACTION_VIEW).setData(
+                        Uri.parse("geo:0,0?q=BuckinghamPalace")),
+                new Intent(Intent.ACTION_VIEW).setData(
+                        Uri.parse("http://example.com/oceans.mp4")).setType("video/mp4"),
+                new Intent(Intent.ACTION_VIEW).setData(
+                        Uri.parse("http://www.example.com/horse.mp3")).setType("audio/*"),
+                new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH),
+                new Intent(Intent.ACTION_VIEW).setData(
+                        Uri.parse("market://details?id=com.android.chrome")).addCategory(
+                        Intent.CATEGORY_BROWSABLE),
+                new Intent(Intent.ACTION_WEB_SEARCH),
+                new Intent(Settings.ACTION_SEARCH_SETTINGS),
+                new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE),
+                new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
+                        Uri.parse("package:com.android.chrome")),
+                new Intent(Intent.ACTION_INSERT).setData(Events.CONTENT_URI),
+                new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
+            ));
 
     // This flag specifies we are dealing with intents fired from the primary profile.
     public static final int FLAG_INTENTS_FROM_PRIMARY = 1;
@@ -187,6 +140,124 @@
 
     IntentFiltersTestHelper(Context context) {
         mContext = context;
+
+        addIntentsThatDependOnDeviceFeatures();
+    }
+
+    private void addIntentsThatDependOnDeviceFeatures() {
+        PackageManager pm = mContext.getPackageManager();
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            forwardedIntentsFromManaged.addAll(Arrays.asList(
+                    new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:123")),
+                    new Intent(Intent.ACTION_CALL).setData(Uri.parse("tel:123")),
+                    new Intent("android.intent.action.CALL_EMERGENCY").setData(
+                            Uri.parse("tel:123")),
+                    new Intent("android.intent.action.CALL_PRIVILEGED").setData(
+                            Uri.parse("tel:123")),
+                    new Intent(Intent.ACTION_VIEW).setData(Uri.parse("tel:123")).addCategory(
+                            Intent.CATEGORY_BROWSABLE),
+                    new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS),
+                    new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS),
+                    new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("sms:07700900100")),
+                    new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("smsto:07700900100")),
+                    new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mms:07700900100")),
+                    new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mmsto:07700900100")),
+                    new Intent(Intent.ACTION_VIEW).setData(
+                            Uri.parse("sms:07700900100?body=Hello%20world")).addCategory(
+                            Intent.CATEGORY_BROWSABLE),
+                    new Intent(Intent.ACTION_VIEW).setData(
+                            Uri.parse("smsto:07700900100?body=Hello%20world")).addCategory(
+                            Intent.CATEGORY_BROWSABLE),
+                    new Intent(Intent.ACTION_VIEW).setData(
+                            Uri.parse("mms:07700900100?body=Hello%20world")).addCategory(
+                            Intent.CATEGORY_BROWSABLE),
+                    new Intent(Intent.ACTION_VIEW).setData(
+                            Uri.parse("mmsto:07700900100?body=Hello%20world")).addCategory(
+                            Intent.CATEGORY_BROWSABLE),
+                    new Intent(Settings.ACTION_APN_SETTINGS)));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
+            forwardedIntentsFromManaged.addAll(Arrays.asList(
+                    new Intent(Settings.ACTION_NFC_SETTINGS),
+                    new Intent(Settings.ACTION_NFCSHARING_SETTINGS),
+                    new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS)));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+            forwardedIntentsFromManaged.add(
+                    new Intent(CardEmulation.ACTION_CHANGE_DEFAULT));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+            forwardedIntentsFromManaged.addAll(Arrays.asList(
+                    new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+                    new Intent(MediaStore.ACTION_VIDEO_CAPTURE),
+                    new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),
+                    new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA),
+                    new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE),
+                    new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)));
+        }
+
+        final String state = Environment.getExternalStorageState();
+        if (Environment.MEDIA_MOUNTED.equals(state)) {
+            forwardedIntentsFromManaged.add(
+                    new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+            forwardedIntentsFromManaged.addAll(Arrays.asList(
+                    new Intent(Settings.ACTION_WIFI_IP_SETTINGS),
+                    new Intent(Settings.ACTION_WIFI_SETTINGS)));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+            forwardedIntentsFromManaged.addAll(Arrays.asList(
+                    new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION),
+                    new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_LOCATION)) {
+            forwardedIntentsFromManaged.add(
+                    new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+            forwardedIntentsFromManaged.addAll(Arrays.asList(
+                    new Intent(Settings.ACTION_SOUND_SETTINGS),
+                    new Intent("android.settings.ACTION_OTHER_SOUND_SETTINGS")));
+            notForwardedIntentsFromManaged.add(
+                    new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_HOME_SCREEN)) {
+            forwardedIntentsFromManaged.add(
+                    new Intent(Settings.ACTION_HOME_SETTINGS));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) {
+            forwardedIntentsFromManaged.addAll(Arrays.asList(
+                    new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
+                    new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS)));
+            notForwardedIntentsFromManaged.add(
+                    new Intent("android.settings.SHOW_INPUT_METHOD_PICKER"));
+        }
+
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            forwardedIntentsFromManaged.add(
+                    new Intent(Settings.ACTION_DREAM_SETTINGS));
+        }
+
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+            forwardedIntentsFromManaged.add(
+                    new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS));
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
+            notForwardedIntentsFromManaged.add(
+                    new Intent(Settings.ACTION_PRINT_SETTINGS));
+        }
     }
 
     public boolean checkCrossProfileIntentFilters(int flag) {
@@ -298,7 +369,7 @@
      * @return {@code null} if all the intents are correctly handled
      *         otherwise, the first intent in the list which is not handled correctly.
      */
-    private Intent checkForIntentsNotHandled(Intent[] intentList,
+    private Intent checkForIntentsNotHandled(ArrayList<Intent> intentList,
             ActivityInfo expectedForwarderActivityInfo, boolean canResolve) {
         for (Intent intent : intentList) {
             if (canForwarderActivityHandleIntent(intent,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
index 70899c6..bca7a66 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
@@ -89,6 +89,22 @@
         if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE && state[0] == PackageManager.PERMISSION_GRANTED) {
             mFingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
             mKeyguardManager = (KeyguardManager) getSystemService(KeyguardManager.class);
+            Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
+
+            if (!mKeyguardManager.isKeyguardSecure()) {
+                // Show a message that the user hasn't set up a lock screen.
+                showToast( "Secure lock screen hasn't been set up.\n"
+                                + "Go to 'Settings -> Security -> Screen lock' to set up a lock screen");
+                startTestButton.setEnabled(false);
+                return;
+            } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+                showToast("No fingerprints enrolled.\n"
+                                + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint");
+                startTestButton.setEnabled(false);
+                return;
+            }
+
+            createKey();
 
             try {
                 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
@@ -107,7 +123,6 @@
                 throw new RuntimeException("Failed to init Cipher", e);
             }
 
-            Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
             startTestButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
@@ -117,21 +132,7 @@
                         showAuthenticationScreen();
                     }
                 }
-
             });
-
-            if (!mKeyguardManager.isKeyguardSecure()) {
-                // Show a message that the user hasn't set up a lock screen.
-                showToast( "Secure lock screen hasn't been set up.\n"
-                                + "Go to 'Settings -> Security -> Screen lock' to set up a lock screen");
-                startTestButton.setEnabled(false);
-            } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
-                showToast("No fingerprints enrolled.\n"
-                                + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint");
-                startTestButton.setEnabled(false);
-            } else {
-                createKey();
-            }
         }
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index 6dbf8cc..2dfc7c8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -287,7 +287,9 @@
 
     @Override
     protected void activityCleanUp() {
-        mScreenManipulator.turnScreenOff();
+        if (mScreenManipulator != null) {
+            mScreenManipulator.turnScreenOff();
+        }
         LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
new file mode 100644
index 0000000..43f293a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.tv.TvContract;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Tests for verifying TV app behavior for TV app-link.
+ */
+public class AppLinkTestActivity extends TvAppVerifierActivity implements View.OnClickListener {
+    private static final long TIMEOUT_MS = 5l * 60l * 1000l;  // 5 mins.
+
+    private boolean mSelectAppLinkItemPassed;
+    private View mSelectAppLinkItem;
+    private View mVerifyAppLinkIntentItem;
+    private View mVerifyAppLinkCardItem;
+
+    Runnable mSelectAppLinkFailCallback;
+
+    @Override
+    public void onClick(View v) {
+        final View postTarget = getPostTarget();
+
+        if (containsButton(mSelectAppLinkItem, v)) {
+            Intent tvAppIntent = null;
+            String[] projection = { TvContract.Channels._ID };
+            try (Cursor cursor = getContentResolver().query(
+                    TvContract.buildChannelsUriForInput(MockTvInputService.getInputId(this)),
+                    projection, null, null, null)) {
+                if (cursor != null && cursor.moveToNext()) {
+                    tvAppIntent = new Intent(Intent.ACTION_VIEW,
+                            TvContract.buildChannelUri(cursor.getLong(0)));
+                }
+            }
+            if (tvAppIntent == null) {
+                Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+                return;
+            }
+
+            mSelectAppLinkFailCallback = new Runnable() {
+                @Override
+                public void run() {
+                    mSelectAppLinkItemPassed = false;
+                    setPassState(mSelectAppLinkItem, false);
+                    setPassState(mVerifyAppLinkIntentItem, false);
+                }
+            };
+            postTarget.postDelayed(mSelectAppLinkFailCallback, TIMEOUT_MS);
+            mSelectAppLinkItemPassed = true;
+            setPassState(mSelectAppLinkItem, true);
+
+            startActivity(tvAppIntent);
+        } else if (containsButton(mVerifyAppLinkCardItem, v)) {
+            setPassState(mVerifyAppLinkCardItem, true);
+            getPassButton().setEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        if (mSelectAppLinkItemPassed
+                && TextUtils.equals(MockTvInputSetupActivity.APP_LINK_TEST_VALUE,
+                        intent.getStringExtra(MockTvInputSetupActivity.APP_LINK_TEST_KEY))) {
+            getPostTarget().removeCallbacks(mSelectAppLinkFailCallback);
+            setPassState(mVerifyAppLinkIntentItem, true);
+            setButtonEnabled(mVerifyAppLinkCardItem, true);
+        }
+    }
+
+    @Override
+    protected void createTestItems() {
+        mSelectAppLinkItem = createUserItem(R.string.tv_app_link_test_select_app_link,
+                R.string.tv_launch_tv_app, this);
+        setButtonEnabled(mSelectAppLinkItem, true);
+        mVerifyAppLinkIntentItem = createAutoItem(
+                R.string.tv_app_link_test_verify_link_clicked);
+        mVerifyAppLinkCardItem = createUserItem(R.string.tv_input_link_test_verify_link_interface,
+                android.R.string.yes, this);
+        TextView instructions = (TextView) mVerifyAppLinkCardItem.findViewById(R.id.instructions);
+        Drawable image = getDrawable(R.drawable.app_link_img);
+        image.setBounds(0, 0, 317, 241);
+        instructions.setCompoundDrawablePadding(10);
+        instructions.setCompoundDrawables(image, null, null, null);
+    }
+
+    @Override
+    protected void setInfoResources() {
+        setInfoResources(R.string.tv_app_link_test, R.string.tv_app_link_test_info, -1);
+    }
+}
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 c05b753..43ed7da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -17,9 +17,12 @@
 package com.android.cts.verifier.tv;
 
 import android.app.Activity;
+import android.content.ComponentName;
 import android.content.ContentUris;
 import android.content.ContentValues;
+import android.content.Intent;
 import android.database.Cursor;
+import android.graphics.Color;
 import android.media.tv.TvContract;
 import android.media.tv.TvContract.Programs;
 import android.media.tv.TvInputInfo;
@@ -28,6 +31,8 @@
 import android.util.Pair;
 import android.view.View;
 
+import com.android.cts.verifier.R;
+
 import java.util.ArrayList;
 
 public class MockTvInputSetupActivity extends Activity {
@@ -38,6 +43,10 @@
 
     /* package-private */ static final String PROGRAM_TITLE = "Dummy Program";
     /* package-private */ static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
+
+    /* package-private */ static final String APP_LINK_TEST_KEY = "app_link_test_key";
+    /* package-private */ static final String APP_LINK_TEST_VALUE = "app_link_test_value";
+    private static final String APP_LINK_TEXT = "Cts App-Link Text";
     private static final long PROGRAM_LENGTH_MILLIS = 60 * 60 * 1000;
     private static final int PROGRAM_COUNT = 24;
 
@@ -69,6 +78,18 @@
             values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
             values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER);
             values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, CHANNEL_NAME);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_TEXT, APP_LINK_TEXT);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_COLOR, Color.RED);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_ICON_URI,
+                    "android.resource://" + getPackageName() + "/" + R.drawable.icon);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+                    "android.resource://" + getPackageName() + "/" + R.raw.sns_texture);
+            Intent appLinkIntentUri = new Intent(this, AppLinkTestActivity.class);
+            appLinkIntentUri.putExtra(APP_LINK_TEST_KEY, APP_LINK_TEST_VALUE);
+            appLinkIntentUri.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_INTENT_URI,
+                    appLinkIntentUri.toUri(Intent.URI_INTENT_SCHEME));
+
             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) {
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 12e9652..acab18e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -35,8 +35,6 @@
 public abstract class TvAppVerifierActivity extends PassFailButtons.Activity {
     private static final String TAG = "TvAppVerifierActivity";
 
-    private static final long TIMEOUT_MS = 5l * 60l * 1000l;  // 5 mins.
-
     private LayoutInflater mInflater;
     private ViewGroup mItemList;
     private View mPostTarget;
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 06f4f6f..e8e2cee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -25,8 +25,6 @@
 
 import com.android.cts.verifier.R;
 
-import java.util.List;
-
 /**
  * Tests for verifying TV app behavior for third-party TV input apps.
  */
@@ -131,7 +129,7 @@
         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);
+                android.R.string.yes, this);
     }
 
     @Override
diff --git a/apps/cts-usb-accessory/Android.mk b/apps/cts-usb-accessory/Android.mk
index 8d18da3..f3a7ebd 100644
--- a/apps/cts-usb-accessory/Android.mk
+++ b/apps/cts-usb-accessory/Android.mk
@@ -33,6 +33,7 @@
 LOCAL_STATIC_LIBRARIES := libusbhost libcutils
 LOCAL_LDLIBS += -lpthread
 LOCAL_CFLAGS := -g -O0
+LOCAL_CXX_STL := none
 
 include $(BUILD_HOST_EXECUTABLE)
 
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
index 70414ca..2ae2e10 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
@@ -294,7 +294,7 @@
     private LocalVolumeInfo getAdoptionVolume() throws Exception {
         String[] lines = null;
         int attempt = 0;
-        while (attempt++ < 5) {
+        while (attempt++ < 15) {
             lines = getDevice().executeShellCommand("sm list-volumes private").split("\n");
             for (String line : lines) {
                 final LocalVolumeInfo info = new LocalVolumeInfo(line.trim());
diff --git a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
index a86f7f0..c460300 100644
--- a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
+++ b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
@@ -19,7 +19,7 @@
     A simple app with a tracing section to test that apps tracing signals are
     emitted by atrace.
     -->
-    <application>
+    <application android:debuggable="true"> <!-- Debuggable to enable tracing -->
         <activity android:name=".AtraceTestAppActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
index 8da189f..1d5dd11 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
@@ -25,11 +25,13 @@
  * apps.
  */
 public class LauncherAppsMultiUserTest extends BaseLauncherAppsTest {
+    private static final String FEATURE_LIVE_TV = "android.software.live_tv";
 
     private int mSecondaryUserId;
     private int mSecondaryUserSerialNumber;
 
     private boolean mMultiUserSupported;
+    private boolean mHasLiveTvFeature;
 
     @Override
     protected void setUp() throws Exception {
@@ -37,6 +39,7 @@
         // We need multi user to be supported in order to create a secondary user
         // and api level 21 to support LauncherApps
         mMultiUserSupported = getMaxNumberOfUsersSupported() > 1 && getDevice().getApiLevel() >= 21;
+        mHasLiveTvFeature = hasDeviceFeature(FEATURE_LIVE_TV);
 
         if (mMultiUserSupported) {
             removeTestUsers();
@@ -58,7 +61,7 @@
     }
 
     public void testGetActivitiesForNonProfileFails() throws Exception {
-        if (!mMultiUserSupported) {
+        if (!mMultiUserSupported || mHasLiveTvFeature) {
             return;
         }
         installApp(SIMPLE_APP_APK);
@@ -73,7 +76,7 @@
     }
 
     public void testNoLauncherCallbackPackageAddedSecondaryUser() throws Exception {
-        if (!mMultiUserSupported) {
+        if (!mMultiUserSupported || mHasLiveTvFeature) {
             return;
         }
         startCallbackService();
diff --git a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
index a0d3167..abb1ac4 100644
--- a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
@@ -589,9 +589,9 @@
         assertDomainN("u:r:zygote:s0", "zygote", "zygote64");
     }
 
-    /* drm server is always present */
+    /* Checks drmserver for devices that require it */
     public void testDrmServerDomain() throws DeviceNotAvailableException {
-        assertDomainOne("u:r:drmserver:s0", "/system/bin/drmserver");
+        assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver");
     }
 
     /* Media server is always running */
diff --git a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
old mode 100644
new mode 100755
index 63c7472..0e11111
--- a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
+++ b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
@@ -77,27 +77,42 @@
      * @param threshold maximum difference per channel
      * @return {@code true} if the images are similar, false otherwise
      */
-    private static boolean compare(BufferedImage expected, BufferedImage actual,
-            int threshold) {
-        final int w = actual.getWidth();
-        final int h = actual.getHeight();
-        if (w != expected.getWidth() || h != expected.getHeight()) {
+    private static int getAlphaScaledBlue(final int color) {
+        return (color & 0x000000FF) * getAlpha(color) / 255;
+    }
+
+    private static int getAlphaScaledGreen(final int color) {
+        return ((color & 0x0000FF00) >> 8) * getAlpha(color) / 255;
+    }
+
+    private static int getAlphaScaledRed(final int color) {
+        return ((color & 0x00FF0000) >> 16) * getAlpha(color) / 255;
+    }
+
+    private static int getAlpha(final int color) {
+        // use logical shift for keeping an unsigned value
+        return (color & 0xFF000000) >>> 24;
+    }
+
+    private static boolean compare(BufferedImage reference, BufferedImage generated, int threshold) {
+        final int w = generated.getWidth();
+        final int h = generated.getHeight();
+        if (w != reference.getWidth() || h != reference.getHeight()) {
             return false;
         }
 
         for (int i = 0; i < w; i++) {
             for (int j = 0; j < h; j++) {
-                final int p1 = expected.getRGB(i, j);
-                final int p2 = actual.getRGB(i, j);
-                final int dr = (p1 & 0x000000FF) - (p2 & 0x000000FF);
-                final int dg = ((p1 & 0x0000FF00) - (p2 & 0x0000FF00)) >> 8;
-                final int db = ((p1 & 0x00FF0000) - (p2 & 0x00FF0000)) >> 16;
-                final int da = ((p1 & 0xFF000000) - (p2 & 0xFF000000)) >> 24;
+                final int p1 = reference.getRGB(i, j);
+                final int p2 = generated.getRGB(i, j);
+
+                final int dr = getAlphaScaledRed(p1) - getAlphaScaledRed(p2);
+                final int dg = getAlphaScaledGreen(p1) - getAlphaScaledGreen(p2);
+                final int db = getAlphaScaledBlue(p1) - getAlphaScaledBlue(p2);
 
                 if (Math.abs(db) > threshold ||
                         Math.abs(dg) > threshold ||
-                        Math.abs(dr) > threshold ||
-                        Math.abs(da) > threshold) {
+                        Math.abs(dr) > threshold) {
                     return false;
                 }
             }
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
index 7e2bfe3..b3ebbad 100755
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -330,13 +330,34 @@
                 ex.release();
             }
         }
-        return false;
+        return true;
     }
 
     public static boolean checkCodecsForPath(Context context, String path) {
         return check(hasCodecsForPath(context, path), "no decoder found");
     }
 
+    public static boolean hasCodecForDomain(boolean encoder, String domain) {
+        for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+            if (encoder != info.isEncoder()) {
+                continue;
+            }
+
+            for (String type : info.getSupportedTypes()) {
+                if (type.toLowerCase().startsWith(domain.toLowerCase() + "/")) {
+                    Log.i(TAG, "found codec " + info.getName() + " for mime " + type);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public static boolean checkCodecForDomain(boolean encoder, String domain) {
+        return check(hasCodecForDomain(encoder, domain),
+                "no " + domain + (encoder ? " encoder" : " decoder") + " found");
+    }
+
     private static boolean hasCodecForMime(boolean encoder, String mime) {
         for (MediaCodecInfo info : sMCL.getCodecInfos()) {
             if (encoder != info.isEncoder()) {
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 345279f..5165544 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -16,6 +16,13 @@
   bug: 17536113
 },
 {
+  description: "the ConnectivityConstraintTest are not yet stable",
+  names: [
+    "android.jobscheduler.cts.ConnectivityConstraintTest"
+  ],
+  bug: 18117279
+},
+{
   description: "tests a fragile by nature as they rely on hardcoded behavior",
   names: [
     "android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverText",
@@ -230,23 +237,6 @@
   bug: 17989532
 },
 {
-  description: "The new AE/AF trigger tests are not yet passing on all devices",
-  names: [
-    "android.hardware.camera2.cts.RobustnessTest#testBasicTriggerSequence",
-    "android.hardware.camera2.cts.RobustnessTest#testSimultaneousTriggers",
-    "android.hardware.camera2.cts.RobustnessTest#testAfThenAeTrigger",
-    "android.hardware.camera2.cts.RobustnessTest#testAeThenAfTrigger"
-  ],
-  bug: 22180706
-},
-{
-  description: "The new create session test is not yet passing on all devices",
-  names: [
-    "android.hardware.camera2.cts.CameraDeviceTest#testCreateSessions"
-  ],
-  bug: 22092756
-},
-{
   description: "The new long processing test is not yet passing on all devices",
   names: [
     "android.hardware.camera2.cts.ImageReaderTest#testLongProcessingRepeatingRaw",
@@ -262,6 +252,13 @@
   bug: 23008511
 },
 {
+  description: "Light status bar CTS coming in late",
+  names: [
+    "com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons"
+  ],
+  bug: 23427621
+},
+{
   description: "known failures",
   names: [
     "android.hardware.cts.SensorBatchingTests#testAccelerometer_50hz_batching",
@@ -299,10 +296,59 @@
 {
   description: "New assist tests that do not yet have a track record.",
   names: [
-    "android.assist.cts.ScreenshotTest",
+    "android.assist.cts.AssistantContentViewTest",
     "android.assist.cts.ExtraAssistDataTest",
-    "android.assist.cts.AssistantContentViewTest"
+    "android.assist.cts.FocusChangeTest",
+    "android.assist.cts.LargeViewHierarchyTest",
+    "android.assist.cts.ScreenshotTest",
+    "android.assist.cts.TextViewTest",
+    "android.assist.cts.WebViewTest"
   ],
   bug: 21668302
+},
+{
+  description: "ConnectivityConstraintTest job scheduler not working.",
+  names: [
+     "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withWifi",
+     "android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintExecutes_withWifi"
+  ],
+  bug: 21262226
+},
+{
+   description: "ConnectivityConstraintTest times out.",
+   names: [
+     "android.jobscheduler.cts.TimingConstraintsTest#testJobParameters_unexpiredDeadline"
+   ],
+   bug: 23144425
+},
+{
+   description: "Telephony returning wrong value.",
+   names: [
+     "android.telephony.cts.CellInfoTest#testCellInfo"
+   ],
+   bug: 23979591
+},
+{
+   description: "Video encoding tests are timing out.",
+   names: [
+     "android.media.cts.VideoEncoderTest#testGoogH264FlexArbitraryW",
+     "android.media.cts.VideoEncoderTest#testGoogH264SurfArbitraryW"
+   ],
+   bug: 23827982
+},
+{
+  description: "tests not yet ready",
+  names: [
+    "android.telecom.cts.OutgoingCallTest#testStartCallWithSpeakerphoneFalse_SpeakerphoneOffInCall",
+    "android.telecom.cts.OutgoingCallTest#testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault"
+  ],
+  bug: 24067587
+},
+{
+  description: "protected broadcast not working",
+  names: [
+   "android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
+  ],
+  bug: 23192492
 }
 ]
diff --git a/tests/tests/netlegacy22/api/Android.mk b/tests/netlegacy22.api/Android.mk
similarity index 100%
rename from tests/tests/netlegacy22/api/Android.mk
rename to tests/netlegacy22.api/Android.mk
diff --git a/tests/tests/netlegacy22/api/AndroidManifest.xml b/tests/netlegacy22.api/AndroidManifest.xml
similarity index 92%
rename from tests/tests/netlegacy22/api/AndroidManifest.xml
rename to tests/netlegacy22.api/AndroidManifest.xml
index d243e45..f13805c 100644
--- a/tests/tests/netlegacy22/api/AndroidManifest.xml
+++ b/tests/netlegacy22.api/AndroidManifest.xml
@@ -16,7 +16,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.net.legacy22">
+    package="com.android.cts.netlegacy22.api">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -30,7 +30,7 @@
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.cts.net.legacy22"
+                     android:targetPackage="com.android.cts.netlegacy22.api"
                      android:label="CTS tests of legacy android.net APIs as of API 22">
         <meta-data android:name="listener"
             android:value="com.android.cts.runner.CtsTestRunListener" />
diff --git a/tests/tests/netlegacy22/api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
similarity index 84%
rename from tests/tests/netlegacy22/api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
rename to tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
index 8a9002b..1836f06 100644
--- a/tests/tests/netlegacy22/api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
+++ b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
@@ -85,24 +85,58 @@
                 ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
     }
 
-    private void checkSourceAddress(String addrString, int type) throws Exception {
-        DatagramSocket d = new DatagramSocket();
-        d.connect(InetAddress.getByName(addrString), 7);
-        InetAddress localAddress = d.getLocalAddress();
-
+    // Returns a list of all the IP addresses for all the networks of a given legacy type. We can't
+    // just fetch the IP addresses for that type because there is no public getLinkProperties API
+    // that takes a legacy type.
+    private List<InetAddress> getIpAddresses(int type) {
+        ArrayList<InetAddress> addresses = new ArrayList<>();
         Network[] networks = mCm.getAllNetworks();
         for (int i = 0; i < networks.length; i++) {
             NetworkInfo ni = mCm.getNetworkInfo(networks[i]);
             if (ni != null && ni.getType() == type) {
+                // This does not include IP addresses on stacked interfaces (e.g., 464xlat), because
+                // there is no public API that will return them.
                 LinkProperties lp = mCm.getLinkProperties(networks[i]);
                 for (LinkAddress address : lp.getLinkAddresses()) {
-                    if (address.getAddress().equals(localAddress)) {
-                        return;
-                    }
+                    addresses.add(address.getAddress());
                 }
             }
         }
-        fail("Local address " + localAddress + " not assigned to any network of type " + type);
+        return addresses;
+    }
+
+    private boolean hasIPv4(int type) {
+        for (InetAddress address : getIpAddresses(type)) {
+            if (address instanceof Inet4Address) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void checkSourceAddress(String addrString, int type) throws Exception {
+        // The public requestRouteToHost API only supports IPv4, but it will not return failure if
+        // the network does not have an IPv4 address. So don't check that it's working unless we
+        // know that the network has an IPv4 address. Note that it's possible that the network will
+        // have an IPv4 address but we don't know about it, because the IPv4 address might be on a
+        // stacked interface and we wouldn't be able to see it.
+        if (!hasIPv4(type)) {
+            Log.d(TAG, "Not checking source address on network type " + type + ", no IPv4 address");
+            return;
+        }
+
+        DatagramSocket d = new DatagramSocket();
+        d.connect(InetAddress.getByName(addrString), 7);
+        InetAddress localAddress = d.getLocalAddress();
+        String localAddrString = localAddress.getHostAddress();
+
+        Log.d(TAG, "Got source address " + localAddrString + " for destination " + addrString);
+
+        assertTrue(
+                "Local address " + localAddress + " not assigned to any network of type " + type,
+                getIpAddresses(type).contains(localAddress));
+
+        Log.d(TAG, "Source address " + localAddress + " found on network type " + type);
     }
 
     /** Test that hipri can be brought up when Wifi is enabled. */
@@ -127,7 +161,6 @@
         assertTrue("Couldn't requestRouteToHost using HIPRI.",
                 mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
 
-        try { Thread.sleep(1000); } catch(Exception e) {}
         checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
         checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
 
diff --git a/tests/tests/netlegacy22/permission/Android.mk b/tests/netlegacy22.permission/Android.mk
similarity index 100%
rename from tests/tests/netlegacy22/permission/Android.mk
rename to tests/netlegacy22.permission/Android.mk
diff --git a/tests/tests/netlegacy22/permission/AndroidManifest.xml b/tests/netlegacy22.permission/AndroidManifest.xml
similarity index 89%
rename from tests/tests/netlegacy22/permission/AndroidManifest.xml
rename to tests/netlegacy22.permission/AndroidManifest.xml
index d407404..cd1d2ba 100644
--- a/tests/tests/netlegacy22/permission/AndroidManifest.xml
+++ b/tests/netlegacy22.permission/AndroidManifest.xml
@@ -16,7 +16,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.permission">
+    package="com.android.cts.netlegacy22.permission">
 
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <application>
@@ -41,8 +41,8 @@
         relies on hidden APIs.
     -->
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.cts.permission"
-                     android:label="CTS tests of com.android.cts.permission">
+                     android:targetPackage="com.android.cts.netlegacy22.permission"
+                     android:label="CTS tests of legacy android.net permissions as of API 22">
         <meta-data android:name="listener"
             android:value="com.android.cts.runner.CtsTestRunListener" />
     </instrumentation>
diff --git a/tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
similarity index 100%
rename from tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
rename to tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
diff --git a/tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
similarity index 100%
rename from tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
rename to tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
diff --git a/tests/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml b/tests/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
index 9906227..229dbfe 100644
--- a/tests/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
+++ b/tests/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
@@ -42,13 +42,13 @@
 
    <EditText
        android:id="@+id/edit"
-       android:layout_width="80dip"
+       android:layout_width="64dip"
        android:layout_height="80dip"
        android:maxLines="1000"
        android:scrollbars="vertical"
        android:focusable="false"
        android:includeFontPadding="false"
-       android:textSize="10dip"
+       android:textSize="8dip"
        android:textStyle="normal"
        android:fontFamily="sans-serif"
        android:visibility="gone"
diff --git a/tests/tests/alarmclock/AndroidTest.xml b/tests/tests/alarmclock/AndroidTest.xml
index 1cdd7f4..aafdb61 100644
--- a/tests/tests/alarmclock/AndroidTest.xml
+++ b/tests/tests/alarmclock/AndroidTest.xml
@@ -17,7 +17,5 @@
     <option name="cts-apk-installer:test-file-name" value="CtsAlarmClockService.apk" />
     <option name="run-command:run-command"
          value="settings put secure voice_interaction_service android.alarmclock.service/.MainInteractionService" />
-    <option name="run-command:teardown-command"
-         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
     <option name="cts-apk-installer:test-file-name" value="CtsAlarmClockTestCases.apk" />
 </configuration>
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
index 4e5b4ce..0d434f2 100644
--- a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
@@ -70,7 +70,7 @@
                 new IntentFilter(Utils.BROADCAST_INTENT + testCaseType.toString()));
     }
 
-    private boolean isIntentAupported(TestcaseType testCaseType) {
+    private boolean isIntentSupported(TestcaseType testCaseType) {
         Intent intent;
         switch (testCaseType) {
           case DISMISS_ALARM:
@@ -102,7 +102,7 @@
     protected String runTest(TestcaseType testCaseType) throws Exception {
         Log.i(TAG, "Begin Testing: " + testCaseType);
         // Make sure the corresponding intent is supported by the platform, before testing.
-        if (!isIntentAupported(testCaseType)) return Utils.COMPLETION_RESULT;
+        if (!isIntentSupported(testCaseType)) return Utils.COMPLETION_RESULT;
 
         if (!startTestActivity(testCaseType)) {
             fail("test activity start failed for testcase = " + testCaseType);
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index b6fef4a..a889c02 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -20,6 +20,7 @@
 import android.app.usage.NetworkStatsManager;
 import android.app.usage.NetworkStats;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -39,7 +40,7 @@
 import java.net.URL;
 import java.text.MessageFormat;
 import java.util.Scanner;
-import javax.net.ssl.HttpsURLConnection;
+import java.net.HttpURLConnection;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -56,14 +57,21 @@
         ConnectivityManager.TYPE_MOBILE,
     };
 
-    // Order corresponds to sNetworkTypesToTest
-    private static final int[] sTransportTypesToTest = new int[] {
-        NetworkCapabilities.TRANSPORT_WIFI,
-        NetworkCapabilities.TRANSPORT_CELLULAR,
+    private static final String[] sSystemFeaturesToTest = new String[] {
+        PackageManager.FEATURE_WIFI,
+        PackageManager.FEATURE_TELEPHONY,
+    };
+
+    private static final String[] sFeatureNotConnectedCause = new String[] {
+        " Please make sure you are connected to a WiFi access point.",
+        " Please make sure you have added a SIM card with data plan to your phone, have enabled " +
+                "data over cellular and in case of dual SIM devices, have selected the right SIM " +
+                "for data connection."
     };
 
     private NetworkStatsManager mNsm;
     private ConnectivityManager mCm;
+    private PackageManager mPm;
     private long mStartTime;
     private long mEndTime;
 
@@ -71,62 +79,50 @@
     private String mWriteSettingsMode;
     private String mUsageStatsMode;
 
-    private void exerciseRemoteHost(int transportType) throws Exception {
+    private void exerciseRemoteHost(Network network) throws Exception {
         final int timeout = 15000;
-        mCm.requestNetwork(new NetworkRequest.Builder()
-            .addTransportType(transportType)
-            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-            .build(), new ConnectivityManager.NetworkCallback() {
-                @Override
-                public void onAvailable(Network network) {
-                    NetworkInfo networkInfo = mCm.getNetworkInfo(network);
-                    if (networkInfo == null) {
-                        Log.w(LOG_TAG, "Network info is null");
-                    } else {
-                        Log.w(LOG_TAG, "Network: " + networkInfo.toString());
-                    }
-                    InputStreamReader in = null;
-                    HttpsURLConnection urlc = null;
-                    String originalKeepAlive = System.getProperty("http.keepAlive");
-                    System.setProperty("http.keepAlive", "false");
-                    try {
-                        urlc = (HttpsURLConnection) network.openConnection(new URL(
-                                "https://www.google.com"));
-                        urlc.setConnectTimeout(timeout);
-                        urlc.setUseCaches(false);
-                        urlc.connect();
-                        boolean ping = urlc.getResponseCode() == 200;
-                        if (ping) {
-                            in = new InputStreamReader(
-                                    (InputStream) urlc.getContent());
-
-                            mBytesRead = 0;
-                            while (in.read() != -1) ++mBytesRead;
-                        }
-                    } catch (Exception e) {
-                        Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
-                    } finally {
-                        if (in != null) {
-                            try {
-                                in.close();
-                            } catch (IOException e) {
-                                // don't care
-                            }
-                        }
-                        if (urlc != null) {
-                            urlc.disconnect();
-                        }
-                        if (originalKeepAlive == null) {
-                            System.clearProperty("http.keepAlive");
-                        } else {
-                            System.setProperty("http.keepAlive", originalKeepAlive);
-                        }
-                    }
-                }
-            });
+        NetworkInfo networkInfo = mCm.getNetworkInfo(network);
+        if (networkInfo == null) {
+            Log.w(LOG_TAG, "Network info is null");
+        } else {
+            Log.w(LOG_TAG, "Network: " + networkInfo.toString());
+        }
+        InputStreamReader in = null;
+        HttpURLConnection urlc = null;
+        String originalKeepAlive = System.getProperty("http.keepAlive");
+        System.setProperty("http.keepAlive", "false");
         try {
-            Thread.sleep(timeout);
-        } catch (InterruptedException e) {
+            urlc = (HttpURLConnection) network.openConnection(new URL(
+                    "http://www.265.com/"));
+            urlc.setConnectTimeout(timeout);
+            urlc.setUseCaches(false);
+            urlc.connect();
+            boolean ping = urlc.getResponseCode() == 200;
+            if (ping) {
+                in = new InputStreamReader(
+                        (InputStream) urlc.getContent());
+
+                mBytesRead = 0;
+                while (in.read() != -1) ++mBytesRead;
+            }
+        } catch (Exception e) {
+            Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    // don't care
+                }
+            }
+            if (urlc != null) {
+                urlc.disconnect();
+            }
+            if (originalKeepAlive == null) {
+                System.clearProperty("http.keepAlive");
+            } else {
+                System.setProperty("http.keepAlive", originalKeepAlive);
+            }
         }
     }
 
@@ -139,6 +135,8 @@
         mCm = (ConnectivityManager) getInstrumentation().getContext()
                 .getSystemService(Context.CONNECTIVITY_SERVICE);
 
+        mPm = getInstrumentation().getContext().getPackageManager();
+
         mWriteSettingsMode = getAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS);
         setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, "allow");
         mUsageStatsMode = getAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS);
@@ -192,14 +190,26 @@
 
     private boolean shouldTestThisNetworkType(int networkTypeIndex, long tolerance)
             throws Exception {
-        NetworkInfo networkInfo = mCm.getNetworkInfo(sNetworkTypesToTest[networkTypeIndex]);
-        if (networkInfo == null || !networkInfo.isAvailable()) {
-            return false;
+        Network[] networks = mCm.getAllNetworks();
+        for (Network network : networks) {
+            NetworkInfo networkInfo = mCm.getNetworkInfo(network);
+            if (networkInfo != null && networkInfo.isConnected() &&
+                    networkInfo.getType() == sNetworkTypesToTest[networkTypeIndex]) {
+                NetworkCapabilities capabilities = mCm.getNetworkCapabilities(network);
+                if (capabilities != null && capabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+                    mStartTime = System.currentTimeMillis() - tolerance;
+                    exerciseRemoteHost(network);
+                    mEndTime = System.currentTimeMillis() + tolerance;
+                    return true;
+                }
+            }
         }
-        mStartTime = System.currentTimeMillis() - tolerance;
-        exerciseRemoteHost(sTransportTypesToTest[networkTypeIndex]);
-        mEndTime = System.currentTimeMillis() + tolerance;
-        return true;
+        assertFalse (sSystemFeaturesToTest[networkTypeIndex] + " is a reported system feature, " +
+                "however no corresponding connected network interface was found. " +
+                sFeatureNotConnectedCause[networkTypeIndex],
+                mPm.hasSystemFeature(sSystemFeaturesToTest[networkTypeIndex]));
+        return false;
     }
 
     private String getSubscriberId(int networkType) {
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index dfa278a..c8ef253 100644
--- a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
+++ b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.DisplayMetrics;
@@ -40,6 +41,8 @@
     }
 
     public static class ExpectedMemorySizesClass {
+        private static final Map<Integer, Integer> expectedMemorySizeForWatch
+            =  new HashMap<Integer, Integer>();
         private static final Map<Integer, Integer> expectedMemorySizeForSmallNormalScreen
             =  new HashMap<Integer, Integer>();
         private static final Map<Integer, Integer> expectedMemorySizeForLargeScreen
@@ -48,6 +51,21 @@
             =  new HashMap<Integer, Integer>();
 
         static {
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_LOW, 32);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_MEDIUM, 32);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_TV, 32);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_HIGH, 36);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_280, 36);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XHIGH, 48);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_360, 48);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_400, 56);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_420, 64);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XXHIGH, 88);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_560, 112);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XXXHIGH, 154);
+        }
+
+        static {
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_LOW, 32);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_MEDIUM, 32);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_TV, 48);
@@ -92,7 +110,15 @@
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XXXHIGH, 768);
         }
 
-        public static Integer getExpectedMemorySize(int screenSize, int screenDensity) {
+        public static Integer getExpectedMemorySize(
+                int screenSize,
+                int screenDensity,
+                boolean isWatch) {
+
+           if (isWatch) {
+              return expectedMemorySizeForWatch.get(screenDensity);
+           }
+
            switch (screenSize) {
                 case Configuration.SCREENLAYOUT_SIZE_SMALL:
                 case Configuration.SCREENLAYOUT_SIZE_NORMAL:
@@ -141,8 +167,11 @@
     }
 
     private void assertMemoryForScreenDensity(int memoryClass, int screenDensity, int screenSize) {
-        int expectedMinimumMemory = ExpectedMemorySizesClass.getExpectedMemorySize(screenSize,
-                                                                                   screenDensity);
+        Context context = getInstrumentation().getTargetContext();
+        boolean isWatch =
+            context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+        int expectedMinimumMemory =
+            ExpectedMemorySizesClass.getExpectedMemorySize(screenSize, screenDensity, isWatch);
 
         assertTrue("Expected to have at least " + expectedMinimumMemory
                 + "mb of memory for screen density " + screenDensity,
diff --git a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
index 5781442..fbb3060 100644
--- a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -74,9 +74,8 @@
         sendNotification(id, R.drawable.black);
         mNotificationManager.cancel(id);
 
-        StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
-        for (StatusBarNotification sbn : sbns) {
-            assertFalse("canceled notification was still alive, id=" + id, sbn.getId() == id);
+        if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
+            fail("canceled notification was still alive, id=" + id);
         }
     }
 
@@ -116,10 +115,31 @@
                 .build();
         mNotificationManager.notify(id, notification);
 
-        StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
-        for (StatusBarNotification sbn : sbns) {
-            if (sbn.getId() == id) return;
+
+        if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
+            fail("couldn't find posted notification id=" + id);
         }
-        fail("couldn't find posted notification id=" + id);
+    }
+
+    private boolean checkNotificationExistence(int id, boolean shouldExist) {
+        // notification is a bit asynchronous so it may take a few ms to appear in getActiveNotifications()
+        // we will check for it for up to 200ms before giving up
+        boolean found = false;
+        for (int tries=3; tries-->0;) {
+            final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+            for (StatusBarNotification sbn : sbns) {
+                if (sbn.getId() == id) {
+                    found = true;
+                    break;
+                }
+            }
+            if (found == shouldExist) break;
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ex) {
+                // pass
+            }
+        }
+        return found == shouldExist;
     }
 }
diff --git a/tests/tests/assist/AndroidManifest.xml b/tests/tests/assist/AndroidManifest.xml
index 1bad800..a81ced2 100644
--- a/tests/tests/assist/AndroidManifest.xml
+++ b/tests/tests/assist/AndroidManifest.xml
@@ -20,7 +20,7 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.INTERNET" />
 
     <application>
       <uses-library android:name="android.test.runner" />
@@ -33,6 +33,9 @@
               <action android:name="android.intent.action.TEST_START_ACTIVITY_LIFECYCLE" />
               <action android:name="android.intent.action.TEST_START_ACTIVITY_SCREENSHOT" />
               <action android:name="android.intent.action.TEST_START_ACTIVITY_EXTRA_ASSIST" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_TEXTVIEW" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_LARGE_VIEW_HIERARCHY" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_WEBVIEW" />
               <category android:name="android.intent.category.LAUNCHER" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
index cc45dbd..54416b4 100644
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -21,7 +21,6 @@
 
 import org.json.JSONException;
 import org.json.JSONObject;
-
 import java.util.ArrayList;
 
 public class Utils {
@@ -36,6 +35,8 @@
     public static final String ACTION_INVALIDATE = "invalidate_action";
     public static final String GET_CONTENT_VIEW_HEIGHT = ACTION_PREFIX + "GET_CONTENT_VIEW_HEIGHT";
     public static final String BROADCAST_CONTENT_VIEW_HEIGHT = ACTION_PREFIX + "VIEW_HEIGHT";
+    public static final String SCROLL_TEXTVIEW_ACTION = ACTION_PREFIX + "TEXTVIEW_SCROLL";
+    public static final String SCROLL_SCROLLVIEW_ACTION = ACTION_PREFIX + "SCROLLVIEW_SCROLL";
     public static final String TEST_ERROR = "Error In Test:";
 
     public static final String ASSIST_STRUCTURE_KEY = "assist_structure";
@@ -46,6 +47,8 @@
     public static final String COMPARE_SCREENSHOT_KEY = "compare_screenshot";
     public static final String DISPLAY_WIDTH_KEY = "display_width";
     public static final String DISPLAY_HEIGHT_KEY = "dislay_height";
+    public static final String SCROLL_X_POSITION = "scroll_x_position";
+    public static final String SCROLL_Y_POSITION = "scroll_y_position";
 
     /** Lifecycle Test intent constants */
     public static final String LIFECYCLE_PREFIX = ACTION_PREFIX + "lifecycle_";
@@ -54,11 +57,14 @@
     public static final String LIFECYCLE_ONSTOP = LIFECYCLE_PREFIX + "onstop";
     public static final String LIFECYCLE_ONDESTROY = LIFECYCLE_PREFIX + "ondestroy";
 
+    /** Focus Change Test intent constants */
+    public static final String GAINED_FOCUS = ACTION_PREFIX + "focus_changed";
+    public static final String LOST_FOCUS = ACTION_PREFIX + "lost_focus";
+
     /** Flag Secure Test intent constants */
     public static final String FLAG_SECURE_HASRESUMED = ACTION_PREFIX + "flag_secure_hasResumed";
-    public static final String APP_3P_HASRESUMED = ACTION_PREFIX + "screenshot_hasResumed";
-    public static final String ASSIST_STRUCTURE_HASRESUMED = ACTION_PREFIX
-            + "assist_structure_hasResumed";
+    public static final String APP_3P_HASRESUMED = ACTION_PREFIX + "app_3p_hasResumed";
+    public static final String TEST_ACTIVITY_LOADED = ACTION_PREFIX + "test_activity_hasResumed";
 
     /** Two second timeout for getting back assist context */
     public static final int TIMEOUT_MS = 2 * 1000;
@@ -80,10 +86,19 @@
     public static final String SCREENSHOT = "SCREENSHOT";
     public static final String EXTRA_ASSIST = "EXTRA_ASSIST";
     public static final String VERIFY_CONTENT_VIEW = "VERIFY_CONTENT_VIEW";
+    public static final String TEXTVIEW = "TEXTVIEW";
+    public static final String LARGE_VIEW_HIERARCHY = "LARGE_VIEW_HIERARCHY";
+    public static final String WEBVIEW = "WEBVIEW";
+    public static final String FOCUS_CHANGE = "FOCUS_CHANGE";
 
     /** Session intent constants */
     public static final String HIDE_SESSION = "android.intent.action.hide_session";
 
+    /** Stub html view to load into WebView */
+    public static final String WEBVIEW_HTML_GREETING = "Hello WebView!";
+    public static final String WEBVIEW_HTML = "<html><body><div><p>" + WEBVIEW_HTML_GREETING
+            + "</p></div></body></html>";
+
     /** Extra data to add to assist data and assist content */
     private static Bundle EXTRA_ASSIST_BUNDLE;
     private static String STRUCTURED_JSON;
@@ -135,6 +150,10 @@
             case SCREENSHOT:
             case EXTRA_ASSIST:
             case VERIFY_CONTENT_VIEW:
+            case TEXTVIEW:
+            case LARGE_VIEW_HIERARCHY:
+            case WEBVIEW:
+            case FOCUS_CHANGE:
                 return "service.DelayedAssistantActivity";
             default:
                 return "";
@@ -147,6 +166,9 @@
     public static final ComponentName getTestAppComponent(String testCaseType) {
         switch (testCaseType) {
             case ASSIST_STRUCTURE:
+            case LARGE_VIEW_HIERARCHY:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.TestApp");
             case DISABLE_CONTEXT:
                 return new ComponentName(
                         "android.assist.testapp", "android.assist.testapp.TestApp");
@@ -162,6 +184,15 @@
             case EXTRA_ASSIST:
                 return new ComponentName(
                         "android.assist.testapp", "android.assist.testapp.ExtraAssistDataActivity");
+            case TEXTVIEW:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.TextViewActivity");
+            case WEBVIEW:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.WebViewActivity");
+            case FOCUS_CHANGE:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.FocusChangeActivity");
             default:
                 return new ComponentName("","");
         }
diff --git a/tests/tests/assist/res/layout/multiple_text_views.xml b/tests/tests/assist/res/layout/multiple_text_views.xml
new file mode 100644
index 0000000..455d5e3
--- /dev/null
+++ b/tests/tests/assist/res/layout/multiple_text_views.xml
@@ -0,0 +1,79 @@
+<?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">
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <ScrollView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1">
+    <TextView
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:scrollbars="vertical"
+      android:text="@string/text_too_large_to_fit" />
+  </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/text_view.xml b/tests/tests/assist/res/layout/text_view.xml
new file mode 100644
index 0000000..9964ab6
--- /dev/null
+++ b/tests/tests/assist/res/layout/text_view.xml
@@ -0,0 +1,42 @@
+<?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">
+    <TextView
+        android:id="@+id/text_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:focusable="false"
+        android:focusableInTouchMode="false"
+        android:scrollbars="vertical"
+        android:clickable="true"
+        android:text="@string/text_too_large_to_fit" />
+
+    <ScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:scrollbars="vertical"
+            android:text="@string/text_too_large_to_fit" />
+    </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/webview.xml b/tests/tests/assist/res/layout/webview.xml
new file mode 100644
index 0000000..bdb8082
--- /dev/null
+++ b/tests/tests/assist/res/layout/webview.xml
@@ -0,0 +1,25 @@
+<?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">
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    </WebView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/values/strings.xml b/tests/tests/assist/res/values/strings.xml
index a84c743..8170d70 100644
--- a/tests/tests/assist/res/values/strings.xml
+++ b/tests/tests/assist/res/values/strings.xml
@@ -15,6 +15,270 @@
 -->
 <resources>
     <string name="welcome">Hello there!</string>
-     <string name="testAppTitle">Assist Structure Test Activity</string>
-     <string name="screenshotActivityTitle">Screenshot Test Activity</string>
+    <string name="testAppTitle">Assist Structure Test Activity</string>
+    <string name="screenshotActivityTitle">Screenshot Test Activity</string>
+    <string name="textViewActivityTitle">TextView Test Activity</string>
+    <string name="webViewActivityTitle">WebView Test Activity</string>
+    <string name="text_too_large_to_fit">❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € →Hello هتاف للترحيب שלום
+        përshëndetje Добры дзень 您好 হ্যালো здравей მიესალმები Χαίρετε હેલો नमस्ते Nnọọ こんにちは ಹಲೋ
+        Сәлеметсіз бе ជំរាបសួរ 안녕하세요 ສະບາຍດີ ഹലോ हॅलो Сайн байна уу नमस्ते سلامהעלא ہیلو
+        မင်္ဂလာပါ ਸਤ ਸ੍ਰੀ ਅਕਾਲ Здравствуйте здраво ආයුබෝවන් ஹலோ హలో สวัสดี Pẹlẹ o
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+        convallis nunc et vestibulum. Sed et consequat quam, blandit varius tortor. Curabitur
+        accumsan nulla lectus, placerat condimentum odio elementum vel. Nulla erat ex, accumsan ut
+        enim sagittis, scelerisque efficitur ante. Nullam quis orci nec magna maximus malesuada ac
+        id sem. Nam sagittis erat risus, a accumsan neque congue sit amet. Nullam risus velit,
+        faucibus eget scelerisque et, maximus eget arcu. Sed porta sed libero ac imperdiet.
+
+        Nulla sem lectus, ullamcorper id dui vel, rutrum interdum augue. Proin aliquam nisi vitae
+        hendrerit tempor. Mauris porttitor velit et egestas feugiat. Vivamus eu dapibus libero,
+        quis fringilla urna. Suspendisse non turpis dui. Vivamus facilisis diam vitae est auctor
+        luctus. Etiam quis lectus viverra, interdum turpis eu, aliquam sem. Nulla vulputate lacinia
+        nisi a dictum. Cras faucibus vitae tortor at ullamcorper. Quisque sit amet sapien maximus,
+        ornare nisi non, imperdiet magna. Vestibulum tempor metus ac mi ultrices dapibus.
+
+        Suspendisse potenti. Mauris pellentesque lacinia tristique. Pellentesque vel dui quis sem
+        lacinia imperdiet feugiat vitae sem. Proin a arcu magna. Sed quis augue eu mi accumsan
+        pellentesque pretium in leo. Duis euismod purus mauris, ac tempor erat auctor non. Quisque
+        bibendum est pulvinar ex dapibus, ac tincidunt nibh tempus. Mauris sodales sem id purus
+        commodo iaculis. Pellentesque a quam dapibus, vehicula lectus at, tincidunt arcu. In
+        placerat porttitor urna quis consequat. Nullam feugiat nisl sed urna hendrerit, sed
+        elementum massa iaculis. Fusce sit amet turpis hendrerit, varius lorem sed, luctus mi.
+        Phasellus sit amet ex orci. Duis scelerisque nisl quis efficitur maximus. Curabitur vitae
+        accumsan nunc, eget varius nisi.
+
+        Fusce efficitur malesuada luctus. Aliquam dapibus tortor sit amet purus semper, sit amet
+        pretium lorem feugiat. Maecenas gravida sed arcu et placerat. Nulla facilisi. Cras placerat
+        rutrum mi, in rutrum mauris maximus at. Mauris eu suscipit ante. Nullam pharetra egestas
+        diam a viverra. Donec sem turpis, tempor malesuada est vel, blandit accumsan magna. In
+        iaculis velit in efficitur hendrerit. Nulla facilisi. Curabitur eget ligula lorem. Sed sit
+        amet dolor ut ligula malesuada condimentum. Phasellus molestie augue eget libero commodo,
+        vel blandit ex blandit.
+
+        Morbi cursus tortor ante, et tempus nisi tempus et. Suspendisse quis gravida diam. Aliquam
+        efficitur dolor sit amet sollicitudin varius. Etiam libero purus, ornare nec nulla vel,
+        ullamcorper blandit nisl. Sed vel consequat diam, id placerat sem. Donec quis elementum
+        urna. In posuere bibendum nunc, in condimentum justo blandit ac. Quisque enim lorem,
+        gravida at purus at, sollicitudin imperdiet erat. Ut consectetur rutrum ante, et pretium
+        odio iaculis a. Nullam a nibh vulputate, volutpat lectus eu, pellentesque felis. Nam
+        vehicula suscipit diam nec convallis. Quisque congue maximus sem, sit amet hendrerit leo
+        tempor et.
+
+        Nam eu consequat dui. Sed semper dignissim mattis. Integer tortor eros, tempor in lectus a,
+        lobortis aliquam dolor. Phasellus at sagittis magna. Nulla eleifend orci ac urna auctor,
+        sit amet luctus urna vulputate. Nulla venenatis venenatis erat ac finibus. Etiam
+        ullamcorper elementum suscipit. Morbi nec velit non mauris porta finibus. Nullam in
+        sagittis odio. Praesent eget nisl ut mauris vestibulum feugiat. Sed vulputate at elit et
+        cursus. Praesent viverra erat blandit nunc egestas, vel feugiat ex condimentum. Class
+        aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+
+        Nulla fermentum mattis urna, non gravida eros vestibulum et. Fusce porttitor augue turpis,
+        sit amet aliquam augue sodales non. Nunc neque odio, sagittis sed gravida euismod, commodo
+        at libero. Donec porttitor pulvinar neque vitae lobortis. Aliquam accumsan velit nec sapien
+        placerat egestas. Aliquam at tincidunt massa, et dignissim justo. Donec sapien ante, rutrum
+        et tristique a, commodo a massa.
+
+        Nunc placerat lobortis magna, et molestie lacus semper porta. Lorem ipsum dolor sit amet,
+        consectetur adipiscing elit. Phasellus ac ligula dui. Duis ultrices viverra eros fermentum
+        finibus. Integer ultrices, felis in accumsan volutpat, mi ligula hendrerit nunc, nec
+        accumsan mauris tellus sit amet metus. Ut pharetra enim et sapien sollicitudin, nec
+        ultricies urna pharetra. Morbi non tortor nec dui feugiat rutrum. Aliquam malesuada sodales
+        risus, sed congue nunc accumsan vitae. Etiam nunc magna, tempus non suscipit eu, feugiat ut
+        nibh. Maecenas et libero ut nisl pellentesque tempor nec vel quam. Etiam sem ligula,
+        ullamcorper non dolor a, viverra placerat nulla. Nullam dictum commodo dui, sed ultrices
+        enim sagittis eget. Morbi non consectetur lectus. In gravida, augue vitae pulvinar
+        molestie, ligula orci vulputate ex, at bibendum urna felis nec nibh. Sed nisl nunc, iaculis
+        at augue venenatis, fringilla accumsan velit. Curabitur nec augue porttitor, rutrum nisi
+        vitae, elementum orci.
+
+        Vestibulum eu tortor iaculis, dignissim velit quis, rhoncus dolor. Donec et tincidunt
+        nulla. Duis faucibus auctor erat ac ultricies. In a fermentum mi. Fusce vitae mi id sem
+        interdum tincidunt. Nulla hendrerit orci turpis, in maximus elit mollis eget. Aliquam erat
+        volutpat. Phasellus mattis est nibh, ut scelerisque ligula egestas eu. Ut molestie orci a
+        malesuada tempor. Sed tempus arcu id orci gravida faucibus. Vivamus ac lacinia neque, at
+        vehicula magna.
+
+        Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Nullam aliquam, justo scelerisque egestas sodales, purus odio posuere arcu, ac ultrices
+        nunc felis non massa. Aliquam vulputate ipsum sed aliquet auctor. In pulvinar, eros sit
+        amet ultricies tristique, lacus ipsum scelerisque eros, nec vestibulum est lectus quis
+        lorem. Pellentesque ac augue ut eros mattis viverra vitae ut lacus. Phasellus imperdiet
+        efficitur elit eget tincidunt. Donec sodales metus at dolor pulvinar, at gravida nibh
+        facilisis. Sed nec tellus luctus, cursus lacus sed, euismod orci. Maecenas sit amet leo
+        orci. Nulla non leo non mi eleifend consequat sit amet vitae dui. Sed gravida gravida
+        justo, tincidunt ultrices justo semper vitae. Fusce at nisi nisl. Morbi molestie quis justo
+        a convallis. Curabitur massa lacus, feugiat quis mauris ac, malesuada viverra est.
+
+        Phasellus bibendum faucibus velit, ac scelerisque velit tincidunt eu. Curabitur quis
+        suscipit erat, ac feugiat odio. Nullam et sapien et nibh maximus posuere. Vivamus faucibus
+        justo eget dictum sollicitudin. Etiam at leo eget elit facilisis lobortis. Maecenas
+        bibendum tortor non erat pretium dignissim. Fusce id imperdiet augue. Suspendisse dignissim
+        magna vel odio viverra varius. Maecenas suscipit ante et lorem sodales vehicula. Quisque
+        vel magna id sem suscipit iaculis. Etiam in elementum risus.
+
+        Suspendisse odio nisi, pharetra et purus sit amet, placerat lobortis diam. Phasellus enim
+        nunc, posuere sed porta in, ornare eu ipsum. Phasellus imperdiet porta neque, vitae dapibus
+        tellus feugiat eget. Nulla sodales leo ac efficitur luctus. Vivamus eget ipsum quis ante
+        pulvinar blandit. Vestibulum a justo convallis justo elementum viverra ut sit amet nisl.
+        Suspendisse eget augue fermentum, sagittis risus a, rhoncus elit. Vestibulum in maximus
+        tortor, non vestibulum libero. Nunc accumsan neque a nisl dapibus, id laoreet neque congue.
+        Pellentesque sapien odio, fringilla non nulla nec, varius placerat diam. Aliquam
+        consectetur neque eu ipsum posuere, nec dignissim sem faucibus. Donec sit amet tempor
+        sapien. Nam at libero vel lorem dapibus ultrices a vel augue. Nunc facilisis justo ante,
+        mollis tristique velit aliquet quis. Mauris consectetur odio at urna bibendum aliquam.
+
+           Nullam lectus orci, hendrerit ut ultrices in, dapibus pellentesque nibh. Aliquam arcu
+        metus, lobortis vel dignissim id, tempus ut ante. Integer vitae ante augue. In hac habitasse
+        platea dictumst. Vestibulum in tellus ante. Cras nisi tellus, congue ac velit quis, rhoncus
+        ornare ligula. Sed facilisis gravida pellentesque. Praesent id ultrices orci, ac ultricies
+        arcu. Donec at ante quis augue faucibus congue. Donec mattis quam dui, ut vestibulum orci
+        tempor mattis. Phasellus in quam id tortor varius ullamcorper ac ac ante. Proin cursus
+        accumsan sem, vel finibus lectus eleifend ut. Donec efficitur feugiat diam id ultricies. In
+        quis euismod nisi. Vestibulum eget viverra sapien. Donec scelerisque nec elit vel viverra.
+
+           Sed mi urna, rutrum quis augue vel, aliquet placerat diam. Proin faucibus in odio et
+        consequat. Proin ut ex in mauris eleifend efficitur. Praesent ullamcorper sollicitudin urna,
+        sed mollis elit hendrerit non. Duis leo lorem, efficitur eu auctor sit amet, scelerisque
+        scelerisque magna. Mauris eget massa auctor, viverra arcu a, elementum nibh. Sed
+        pellentesque, nulla sed condimentum posuere, tortor metus congue sem, nec placerat neque
+        magna vitae purus.
+
+           Etiam at risus vitae sapien aliquam condimentum. Vestibulum id libero placerat purus
+        vehicula consectetur. Pellentesque sapien sapien, posuere at pulvinar at, ultrices sed est.
+        Maecenas nec condimentum ante. Aenean volutpat, ex condimentum hendrerit hendrerit, quam
+        nisl  pharetra nibh, vitae bibendum nisl odio vel lacus. Morbi mi tellus, bibendum id mauris
+        eu,  facilisis volutpat turpis. Maecenas rutrum convallis felis. Quisque eget feugiat felis.
+        Duis pellentesque iaculis massa ut facilisis. Donec nec commodo magna. Integer aliquet orci
+        a odio eleifend elementum. Quisque molestie, urna ut molestie eleifend, odio neque maximus
+        enim, eget viverra metus lectus eget quam. Fusce nec urna ac neque bibendum aliquam vel quis
+        dui. Fusce ac quam consequat, feugiat leo vitae, auctor felis.
+
+           Sed ac metus mauris. Sed sed velit ut tortor aliquam vestibulum at eu arcu. Etiam eu
+        posuere lacus. Maecenas id lacus quis sem mollis sodales. Quisque justo sapien, vulputate ac
+        mi ut, congue vestibulum orci. Donec euismod erat rutrum, laoreet urna sed, accumsan purus.
+        Donec eu quam a sapien condimentum accumsan. Sed at tellus lorem. Curabitur bibendum, arcu
+        sit amet finibus sodales, mi sem finibus sem, eget scelerisque tellus neque vel urna.
+        Suspendisse eu augue nec erat suscipit luctus sed non metus.
+
+           Suspendisse porttitor ex ipsum. Pellentesque tristique eros sed pharetra porttitor.
+        Quisque ut elit vehicula, aliquet est nec, faucibus tellus. Donec ex augue, congue eu
+        dignissim  maximus, vestibulum at purus. Nulla quam enim, laoreet sit amet molestie vel,
+        dapibus nec tortor. Sed interdum massa ac orci gravida, vel viverra lacus lacinia. Donec
+        nisl lacus, fermentum at faucibus ac, consequat ut nibh. Praesent laoreet est augue, vitae
+        maximus dui efficitur sit amet. Cras ipsum tellus, tincidunt at volutpat non, tincidunt ut
+        elit. Morbi commodo sagittis gravida. Pellentesque sed ante massa. Phasellus a turpis non
+        turpis cursus consequat sed nec tortor. Proin et augue elit.
+
+           Duis finibus sem commodo rutrum pulvinar. In sollicitudin ante magna, vel facilisis
+        tellus fringilla vel. Nam purus ex, tincidunt eget varius at, euismod nec elit. Curabitur
+        consequat nulla vel nisi iaculis, ut mattis odio congue. Nulla et mollis tortor, a maximus
+        justo. Donec semper eros sed nunc rhoncus condimentum. Donec nibh purus, interdum non lectus
+        id, porta convallis eros.
+
+           Sed hendrerit, dui non sagittis sollicitudin, enim ex imperdiet sapien, et maximus lorem
+        dolor a enim. Nulla risus nisl, vestibulum at ornare posuere, congue in felis. Duis sagittis
+        id diam a varius. Donec viverra eu orci sodales commodo. Cras metus tortor, sodales vitae
+        auctor non, scelerisque a ante. Quisque sodales nisi libero, ut lobortis enim suscipit ut.
+        Cras mi ipsum, maximus non bibendum sit amet, pharetra quis ipsum. Vestibulum venenatis, odi
+        at hendrerit pretium, tellus diam auctor justo, non posuere quam mauris id nisl. Nam dolor
+        nibh, molestie et lectus et, scelerisque porta elit. Vestibulum viverra condimentum auctor.
+        In eros tortor, convallis sed quam eu, ultrices malesuada purus. Integer lorem quam,
+        ultricies at est consectetur, sagittis porttitor eros. Proin non risus vitae lacus
+        consectetur malesuada non pulvinar justo. Aliquam mollis nisi nunc, sit amet vulputate metus
+        sollicitudin vel.
+
+           Quisque auctor varius fermentum. Praesent mollis justo sit amet est consectetur, in
+        volutpat tellus mollis. Aenean at bibendum eros, at finibus nibh. Phasellus nec mi sem.
+        Mauris pellentesque dui sit amet lobortis aliquam. Ut nec massa at urna aliquam gravida vel
+        in magna. Donec consectetur sapien magna, a auctor sapien placerat eu.
+
+           Pellentesque aliquet ante sed lacus gravida rutrum. Maecenas euismod varius felis, nec
+        tempus metus tempus et. Nam convallis augue a massa scelerisque, vel pharetra dolor
+        scelerisque. Fusce porttitor mi a magna rutrum condimentum. Aliquam fermentum at turpis at
+        auctor. Nulla ut suscipit dui. Donec rutrum viverra aliquam. Donec elementum nisl sapien, ac
+        blandit risus porta facilisis. Proin tellus dolor, ornare vel magna sit amet, maximus
+        volutpat felis. Aenean euismod aliquet purus, at finibus nunc elementum eu. Ut faucibus
+        ullamcorper consectetur. Aenean egestas arcu id mauris elementum, at sollicitudin est
+        congue. Sed a odio mattis, sollicitudin erat ut, tristique dolor. Aliquam luctus risus quis
+        tellus semper, a vestibulum nulla viverra.
+
+           Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
+        himenaeos. Vestibulum sit amet nisi felis. Praesent condimentum consequat lacus pulvinar
+        imperdiet. Praesent vel condimentum quam. Maecenas eu aliquet odio. Vestibulum sed nulla
+        mattis lacus porta bibendum a ac eros. Nunc porttitor sagittis laoreet. Duis porta eros at
+        congue tristique. Pellentesque quis fringilla neque, a hendrerit tellus. Pellentesque ac
+        nibh ac tellus pulvinar porttitor et in est. Integer blandit lorem libero, eu pulvinar
+        tellus posuere eget. Vivamus pretium nulla ligula, ut dapibus massa fringilla in.
+        Suspendisse consectetur sem non elit porta, id pellentesque erat dapibus. Quisque ex mi,
+        tempus et hendrerit nec, gravida quis odio. Ut at mi in leo scelerisque venenatis.
+
+           Ut sed tellus in risus tincidunt tempor ut at arcu. Maecenas ut convallis justo. In
+        rutrum urna eu massa rhoncus, eget condimentum augue vehicula. Nullam eget placerat erat.
+        Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+        Aenean at volutpat orci, a lobortis dolor. Sed consequat facilisis interdum. Fusce libero
+        neque, fringilla in congue a, vehicula rutrum ipsum. Nam ornare placerat vestibulum. Proin
+        nec orci velit. Pellentesque scelerisque gravida diam, ut tristique libero tempus eu. Nam
+        semper lacus nec nulla volutpat imperdiet non eget tortor. Sed et pellentesque ligula.
+
+           Aenean a dolor dolor. Curabitur ut placerat lacus, sit amet aliquet orci. Aliquam erat
+        volutpat. Cras mollis sit amet lectus ornare pretium. Vestibulum fringilla orci vel est
+        iaculis, at mattis quam condimentum. Vivamus semper elit consectetur lectus gravida, in
+        sollicitudin mi fringilla. Donec eget lorem in orci blandit aliquam. Pellentesque libero
+        tellus, dignissim id augue et, vulputate viverra nisl. Cum sociis natoque penatibus et
+        magnis dis parturient montes, nascetur ridiculus mus. Donec ac vulputate metus, eu suscipit
+        sem. Donec placerat, nulla at sodales hendrerit, orci tortor vestibulum purus, non pharetra
+        nulla purus posuere arcu.
+
+           Quisque feugiat elit sem, ac interdum diam pharetra nec. Curabitur sem libero, vulputate
+        eu libero vitae, volutpat facilisis ligula. Aenean maximus erat laoreet, interdum ante in,
+        ultrices nisi. Nullam nec efficitur sapien. Integer feugiat et tortor ac bibendum. Donec a
+        scelerisque tortor. Cras quis viverra diam, vitae viverra ipsum. Aliquam ultrices neque sem,
+        congue sodales elit tempus sit amet. Pellentesque habitant morbi tristique senectus et netus
+        et malesuada fames ac turpis egestas. Sed dignissim ipsum eget diam rhoncus, ut finibus
+        nulla pretium. Morbi suscipit nibh vel nisl posuere molestie. Maecenas posuere turpis et
+        rutrum consectetur. Morbi venenatis arcu non gravida vulputate. Vivamus auctor tellus
+        ullamcorper ligula vestibulum cursus. Nunc gravida sit amet nisl quis facilisis.
+
+           Praesent ut justo vestibulum, accumsan mi et, feugiat purus. Nullam pulvinar iaculis
+        pharetra. Aliquam pulvinar risus sit amet elit suscipit tincidunt. In hac habitasse platea
+        dictumst. Etiam eget velit ac magna lacinia efficitur. Vestibulum ante ipsum primis in
+        faucibus orci luctus et ultrices posuere cubilia Curae; Cras volutpat tempus sollicitudin.
+        Ut et ante elit.
+
+           Sed ac tortor justo. Fusce sed metus libero. Duis sagittis tortor ac ante sollicitudin,
+        nec efficitur nibh euismod. Donec porttitor cursus quam, in aliquam lorem feugiat ut.
+        Aliquam tempor lacus eu feugiat feugiat. Nunc pulvinar, libero at auctor commodo, metus diam
+        commodo lorem, in feugiat elit ex non ligula. Quisque at vestibulum sapien, nec facilisis
+        neque. Aenean luctus, arcu ut rhoncus luctus, est massa rhoncus mauris, nec luctus urna sem
+        quis massa. Nam elit felis, congue et ligula eget, ultricies tincidunt erat. Vivamus
+        eleifend no dui ac dictum. In nulla justo, pulvinar ut tristique sed, congue et orci.
+
+           Quisque imperdiet mi lectus, ac scelerisque augue posuere ut. Duis non pulvinar ipsum,
+        finibus risus. Donec ullamcorper nisl at sodales lobortis. Mauris neque leo, vestibulum sit
+        amet placerat vel, aliquet vel sapien. Morbi massa tellus, scelerisque quis nisl in, feugiat
+        posuere augue. Aenean congue sem ut ipsum vulputate rhoncus vitae at eros. Maecenas in velit
+        orci pellentesque lobortis ac at felis. Vestibulum odio quam, lacinia dapibus ornare eu,
+        vulputate a eros. Curabitur eleifend ornare tellus, non sollicitudin justo viverra in. Sed
+        sodales neque et lacus semper, in pharetra est consequat. Nunc vehicula volutpat lectus, sit
+        amet scelerisque nisi. Aenean sollicitudin, sem at ultricies efficitur, eros metus
+        nisl, et fringilla felis lacus non orci. Praesent eros libero, finibus in purus id,
+        tempor ipsum. Donec suscipit libero velit. Aliquam quis diam pharetra, cursus ipsum in,
+        gravida metus. Maecenas ultrices ligula a ullamcorper scelerisque.
+
+           Donec tincidunt felis turpis, id venenatis neque convallis in. Proin euismod ligula nec
+        urna vulputate, sed elementum mauris ultrices. Ut efficitur sem vel mi vestibulum placerat.
+        Sed fermentum lacus nec metus dictum, a commodo quam fermentum. Sed vel vulputate magna.
+        Integer convallis nisi sit amet mi lobortis pellentesque. In egestas porttitor elit eu
+        scelerisque. Suspendisse eleifend vel enim quis tincidunt. Sed placerat risus et pretium
+        porttitor. Nam justo mi, cursus eu pellentesque vel, bibendum ut nisl. Nulla condimentum
+        lorem, non sagittis lorem volutpat vitae. Mauris nec libero lorem. Vestibulum lacus ex,
+        vulputate non massa vitae, pellentesque vestibulum dolor.
+
+           Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Suspendisse vitae erat nisl. Vestibulum elit ante, semper et semper sit amet, fringilla
+        sapien. Morbi ac nisi sit amet turpis tincidunt mattis ac eget nisl. Nunc a venenatis quam,
+        facilisis maximus odio. Aliquam erat volutpat. Maecenas leo enim, ornare a magna quis,
+        venenatis ornare nulla. Aliquam venenatis nibh et elit tincidunt, ut egestas lorem finibus.
+        Integer iaculis dolor sed enim blandit vestibulum. Nullam vel libero ultricies, sagittis
+        tortor non, molestie eros.</string>
 </resources>
diff --git a/tests/tests/assist/service/AndroidManifest.xml b/tests/tests/assist/service/AndroidManifest.xml
index eea1249..354d771 100644
--- a/tests/tests/assist/service/AndroidManifest.xml
+++ b/tests/tests/assist/service/AndroidManifest.xml
@@ -47,6 +47,8 @@
               <action android:name="android.intent.action.START_TEST_FLAG_SECURE" />
               <action android:name="android.intent.action.START_TEST_SCREENSHOT" />
               <action android:name="android.intent.action.START_TEST_EXTRA_ASSIST" />
+              <action android:name="android.intent.action.START_TEST_TEXTVIEW" />
+              <action android:name="android.intent.action.START_TEST_LARGE_VIEW_HIERARCHY" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
       </activity>
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
index b35e43b..4c1b1f3 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -67,7 +67,7 @@
         }
         mReceiver = new AssistStructureTestBroadcastReceiver();
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Utils.ASSIST_STRUCTURE_HASRESUMED);
+        filter.addAction(Utils.APP_3P_HASRESUMED);
         filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
         mContext.registerReceiver(mReceiver, filter);
     }
@@ -96,7 +96,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(Utils.ASSIST_STRUCTURE_HASRESUMED)) {
+            if (action.equals(Utils.APP_3P_HASRESUMED)) {
                 if (mHasResumedLatch != null) {
                     mHasResumedLatch.countDown();
                 }
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index ecbd435..46fb8d9 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -35,6 +35,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.test.ActivityInstrumentationTestCase2;
@@ -44,8 +45,12 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.webkit.WebView;
+import android.widget.EditText;
 import android.widget.TextView;
 
+import java.lang.Math;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -175,9 +180,9 @@
             mContext.unregisterReceiver(mReceiver);
         }
         mReceiver = new TestResultsReceiver();
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Utils.BROADCAST_ASSIST_DATA_INTENT);
-        mContext.registerReceiver(mReceiver, filter);
+        mContext.registerReceiver(mReceiver,
+                new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
+
         if (!mLatch.await(Utils.getAssistDataTimeout(mTestName), TimeUnit.MILLISECONDS)) {
             fail("Fail to receive broadcast in " + Utils.getAssistDataTimeout(mTestName) + "msec");
         }
@@ -198,26 +203,43 @@
 
         if ((mAssistContent == null) != isContentNull) {
             fail(String.format("Should %s have been null - AssistContent: %s",
-                    isContentNull? "":"not", mAssistContent));
+                    isContentNull ? "" : "not", mAssistContent));
         }
 
         if ((mAssistStructure == null) != isStructureNull) {
             fail(String.format("Should %s have been null - AssistStructure: %s",
-                isStructureNull ? "" : "not", mAssistStructure));
+                    isStructureNull ? "" : "not", mAssistStructure));
         }
 
         if ((mAssistBundle == null) != isBundleNull) {
             fail(String.format("Should %s have been null - AssistBundle: %s",
-                    isBundleNull? "":"not", mAssistBundle));
+                    isBundleNull ? "" : "not", mAssistBundle));
         }
 
         if (mScreenshot == isScreenshotNull) {
             fail(String.format("Should %s have been null - Screenshot: %s",
-                    isScreenshotNull? "":"not", mScreenshot));
+                    isScreenshotNull ? "":"not", mScreenshot));
         }
     }
 
     /**
+     * Sends a broadcast with the specified scroll positions to the test app.
+     */
+    protected void scrollTestApp(int scrollX, int scrollY, boolean scrollTextView,
+            boolean scrollScrollView) {
+        mTestActivity.scrollText(scrollX, scrollY, scrollTextView, scrollScrollView);
+        Intent intent = null;
+        if (scrollTextView) {
+            intent = new Intent(Utils.SCROLL_TEXTVIEW_ACTION);
+        } else if (scrollScrollView) {
+            intent = new Intent(Utils.SCROLL_SCROLLVIEW_ACTION);
+        }
+        intent.putExtra(Utils.SCROLL_X_POSITION, scrollX);
+        intent.putExtra(Utils.SCROLL_Y_POSITION, scrollY);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
      * Verifies the view hierarchy of the backgroundApp matches the assist structure.
      *
      * @param backgroundApp ComponentName of app the assistant is invoked upon
@@ -226,7 +248,7 @@
     protected void verifyAssistStructure(ComponentName backgroundApp, boolean isSecureWindow) {
         // Check component name matches
         assertEquals(backgroundApp.flattenToString(),
-            mAssistStructure.getActivityComponent().flattenToString());
+                mAssistStructure.getActivityComponent().flattenToString());
 
         Log.i(TAG, "Traversing down structure for: " + backgroundApp.flattenToString());
         mView = mTestActivity.findViewById(android.R.id.content).getRootView();
@@ -288,9 +310,10 @@
                         + ((ViewGroup) parentView).getChildAt(childInt).getClass().getName());
             }
         }
+        String parentViewId = null;
         if (parentView.getId() > 0) {
-            Log.i(TAG, "View ID: "
-                    + mTestActivity.getResources().getResourceEntryName(parentView.getId()));
+            parentViewId = mTestActivity.getResources().getResourceEntryName(parentView.getId());
+            Log.i(TAG, "View ID: " + parentViewId);
         }
 
         Log.i(TAG, "parentNode is of type: " + parentNode.getClassName());
@@ -299,7 +322,9 @@
                     "nodechild" + nodeInt + " is of type: "
                     + parentNode.getChildAt(nodeInt).getClassName());
         }
-            Log.i(TAG, "Node ID: " + parentNode.getIdEntry());
+        Log.i(TAG, "Node ID: " + parentNode.getIdEntry());
+
+        assertEquals("IDs do not match", parentViewId, parentNode.getIdEntry());
 
         int numViewChildren = 0;
         int numNodeChildren = 0;
@@ -309,30 +334,56 @@
         numNodeChildren = parentNode.getChildCount();
 
         if (isSecureWindow) {
+            assertTrue("ViewNode property isAssistBlocked is false", parentNode.isAssistBlocked());
             assertEquals("Secure window should only traverse root node.", 0, numNodeChildren);
             isSecureWindow = false;
-            return;
+        } else if (parentNode.getClassName().equals("android.webkit.WebView")) {
+            // WebView will also appear to have no children while the node does, traverse node
+            assertTrue("AssistStructure returned a WebView where the view wasn't one",
+                    parentView instanceof WebView);
+
+            boolean textInWebView = false;
+
+            for (int i = numNodeChildren - 1; i >= 0; i--) {
+               textInWebView |= traverseWebViewForText(parentNode.getChildAt(i));
+            }
+            assertTrue("Did not find expected strings inside WebView", textInWebView);
         } else {
             assertEquals("Number of children did not match.", numViewChildren, numNodeChildren);
-        }
 
-        verifyViewProperties(parentView, parentNode);
+            verifyViewProperties(parentView, parentNode);
 
-        if (parentView instanceof ViewGroup) {
-            parentGroup = (ViewGroup) parentView;
+            if (parentView instanceof ViewGroup) {
+                parentGroup = (ViewGroup) parentView;
 
-            // TODO: set a max recursion level
-            for (int i = numNodeChildren - 1; i >= 0; i--) {
-                View childView = parentGroup.getChildAt(i);
-                ViewNode childNode = parentNode.getChildAt(i);
+                // TODO: set a max recursion level
+                for (int i = numNodeChildren - 1; i >= 0; i--) {
+                    View childView = parentGroup.getChildAt(i);
+                    ViewNode childNode = parentNode.getChildAt(i);
 
-                // if isSecureWindow, should not have reached this point.
-                assertFalse(isSecureWindow);
-                traverseViewAndStructure(childView, childNode, isSecureWindow);
+                    // if isSecureWindow, should not have reached this point.
+                    assertFalse(isSecureWindow);
+                    traverseViewAndStructure(childView, childNode, isSecureWindow);
+                }
             }
         }
     }
 
+    /** 
+     * Return true if the expected strings are found in the WebView, else fail.
+     */
+    private boolean traverseWebViewForText(ViewNode parentNode) {
+        boolean textFound = false;
+        if (parentNode.getText() != null 
+                && parentNode.getText().toString().equals(Utils.WEBVIEW_HTML_GREETING)) {
+            return true;
+        }
+        for (int i = parentNode.getChildCount() - 1; i >= 0; i--) {
+            textFound |= traverseWebViewForText(parentNode.getChildAt(i));
+        }
+        return textFound;
+    }
+
     /**
      * Compare view properties of the view hierarchy with that reported in the assist structure.
      */
@@ -357,21 +408,43 @@
             Log.i(TAG, "view text: " + ((TextView) parentView).getText());
         }
 
-        assertEquals("Scroll X does not match.",
-                parentView.getScrollX(), parentNode.getScrollX());
 
-        assertEquals("Scroll Y does not match.",
-                parentView.getScrollY(), parentNode.getScrollY());
-
-        assertEquals("Heights do not match.", parentView.getHeight(),
-                parentNode.getHeight());
-
+        assertEquals("Scroll X does not match.", parentView.getScrollX(), parentNode.getScrollX());
+        assertEquals("Scroll Y does not match.", parentView.getScrollY(), parentNode.getScrollY());
+        assertEquals("Heights do not match.", parentView.getHeight(), parentNode.getHeight());
         assertEquals("Widths do not match.", parentView.getWidth(), parentNode.getWidth());
 
-        // TODO: handle unicode/i18n
         if (parentView instanceof TextView) {
-            assertEquals("Text in TextView does not match.",
-                    ((TextView) parentView).getText().toString(), parentNode.getText());
+            if (parentView instanceof EditText) {
+                assertEquals("Text selection start does not match",
+                    ((EditText)parentView).getSelectionStart(), parentNode.getTextSelectionStart());
+                assertEquals("Text selection end does not match",
+                        ((EditText)parentView).getSelectionEnd(), parentNode.getTextSelectionEnd());
+            }
+            TextView textView = (TextView) parentView;
+            assertEquals(textView.getTextSize(), parentNode.getTextSize());
+            String viewString = textView.getText().toString();
+            String nodeString = parentNode.getText().toString();
+
+            if (parentNode.getScrollX() == 0 && parentNode.getScrollY() == 0) {
+                Log.i(TAG, "Verifying text within TextView at the beginning");
+                Log.i(TAG, "view string: " + viewString);
+                Log.i(TAG, "node string: " + nodeString);
+                assertTrue("String length is unexpected: original string - " + viewString.length() +
+                                ", string in AssistData - " + nodeString.length(),
+                        viewString.length() >= nodeString.length());
+                assertTrue("Expected a longer string to be shown. expected: "
+                                + Math.min(viewString.length(), 30) + " was: " + nodeString
+                                .length(),
+                        nodeString.length() >= Math.min(viewString.length(), 30));
+                for (int x = 0; x < parentNode.getText().length(); x++) {
+                    assertEquals("Char not equal at index: " + x,
+                            ((TextView) parentView).getText().toString().charAt(x),
+                            parentNode.getText().charAt(x));
+                }
+            } else if (parentNode.getScrollX() == parentView.getWidth()) {
+
+            }
         } else {
             assertNull(parentNode.getText());
         }
diff --git a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
new file mode 100644
index 0000000..f6b90b9
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.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 android.assist.cts;
+
+import android.assist.cts.TestStartActivity;
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Test that triggering the Assistant causes the underlying Activity to lose focus **/
+
+public class FocusChangeTest extends AssistTestBase {
+    private static final String TAG = "FocusChangeTest";
+    private static final String TEST_CASE_TYPE = Utils.FOCUS_CHANGE;
+
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasGainedFocusLatch = new CountDownLatch(1);
+    private CountDownLatch mHasLostFocusLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new FocusChangeTestReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.GAINED_FOCUS);
+        filter.addAction(Utils.LOST_FOCUS);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitToGainFocus() throws Exception {
+        Log.i(TAG, "Waiting for the underlying activity to gain focus before continuing.");
+        if (!mHasGainedFocusLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to gain focus in " + Utils.TIMEOUT_MS + "msec.");
+        }
+    }
+
+    private void waitToLoseFocus() throws Exception {
+        Log.i(TAG, "Waiting for the underlying activity to lose focus.");
+        if (!mHasLostFocusLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity maintained focus despite the Assistant Firing"
+                 + Utils.TIMEOUT_MS + "msec.");
+        }
+    }
+
+    public void testLayerCausesUnderlyingActivityToLoseFocus() throws Exception {
+        mTestActivity.startTest(Utils.FOCUS_CHANGE);
+        waitForAssistantToBeReady(mReadyLatch);
+        mTestActivity.start3pApp(Utils.FOCUS_CHANGE);
+        waitToGainFocus();
+        startSession();
+        waitToLoseFocus();
+    }
+
+    private class FocusChangeTestReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.GAINED_FOCUS) && mHasGainedFocusLatch != null) {
+                mHasGainedFocusLatch.countDown();
+            } else if (action.equals(Utils.LOST_FOCUS) && mHasLostFocusLatch != null) {
+                mHasLostFocusLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
new file mode 100644
index 0000000..bc2ab80
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *  Test that the AssistStructure returned is properly formatted.
+ */
+
+public class LargeViewHierarchyTest extends AssistTestBase {
+    private static final String TAG = "LargeViewHierarchyTest";
+    private static final String TEST_CASE_TYPE = Utils.LARGE_VIEW_HIERARCHY;
+
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    public LargeViewHierarchyTest() {
+        super();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new LargeViewTestBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+    }
+
+    public void testTextView() throws Exception {
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistDataNullness(false, false, false, false);
+
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+                false /*FLAG_SECURE set*/);
+    }
+
+    private class LargeViewTestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+                mHasResumedLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) && mReadyLatch != null) {
+                mReadyLatch.countDown();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index 0a23bfb..c886b74 100644
--- a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -112,7 +112,7 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(action_hasResumed) && mHasResumedLatch != null) {
-                    mHasResumedLatch.countDown();
+                mHasResumedLatch.countDown();
             } else if (action.equals(action_onPause) && mActivityLifecycleLatch != null) {
                 mActivityLifecycleLatch.countDown();
             } else if (action.equals(action_onStop) && mActivityLifecycleLatch != null) {
diff --git a/tests/tests/assist/src/android/assist/cts/TestStartActivity.java b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
index 601b101..e54e774 100644
--- a/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
+++ b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
@@ -23,28 +23,61 @@
 import android.app.assist.AssistStructure.ViewNode;
 import android.content.Intent;
 import android.content.ComponentName;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ScrollView;
 import android.widget.TextView;
 
+import java.lang.Override;
+
 public class TestStartActivity extends Activity {
     static final String TAG = "TestStartActivity";
 
+    private ScrollView mScrollView;
+    private TextView mTextView;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         Log.i(TAG, " in onCreate");
-
         // Set the respective view we want compared with the test activity
-        String testCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
-        switch (testCaseName) {
+        String testName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+        switch (testName) {
             case Utils.ASSIST_STRUCTURE:
                 setContentView(R.layout.test_app);
                 setTitle(R.string.testAppTitle);
                 return;
+            case Utils.TEXTVIEW:
+                setContentView(R.layout.text_view);
+                mTextView =  (TextView) findViewById(R.id.text_view);
+                mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+                setTitle(R.string.textViewActivityTitle);
+                return;
+            case Utils.LARGE_VIEW_HIERARCHY:
+                setContentView(R.layout.multiple_text_views);
+                setTitle(R.string.testAppTitle);
+                return;
+            case Utils.WEBVIEW:
+                if (getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_WEBVIEW)) {
+                    setContentView(R.layout.webview);
+                    setTitle(R.string.webViewActivityTitle);
+                    WebView webview = (WebView) findViewById(R.id.webview);
+                    webview.setWebViewClient(new WebViewClient() {
+                        @Override
+                        public void onPageFinished(WebView view, String url) {
+                            sendBroadcast(new Intent(Utils.TEST_ACTIVITY_LOADED));
+                        }
+                    });
+                    webview.loadData(Utils.WEBVIEW_HTML, "text/html", "UTF-8");
+                }
+                return;
         }
     }
 
@@ -66,6 +99,7 @@
 
     public void start3pApp(String testCaseName) {
         Intent intent = new Intent();
+        intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
         intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
         intent.setComponent(Utils.getTestAppComponent(testCaseName));
         startActivity(intent);
@@ -79,12 +113,14 @@
         startActivity(intent);
     }
 
-    @Override protected void onPause() {
+    @Override
+    protected void onPause() {
         Log.i(TAG, " in onPause");
         super.onPause();
     }
 
-    @Override protected void onStart() {
+    @Override
+    protected void onStart() {
         super.onStart();
         Log.i(TAG, " in onStart");
     }
@@ -94,7 +130,8 @@
         Log.i(TAG, " in onRestart");
     }
 
-    @Override protected void onStop() {
+    @Override
+    protected void onStop() {
         Log.i(TAG, " in onStop");
         super.onStop();
     }
@@ -104,4 +141,25 @@
         Log.i(TAG, " in onDestroy");
         super.onDestroy();
     }
+
+    public void scrollText(int scrollX, int scrollY, boolean scrollTextView,
+            boolean scrollScrollView) {
+        if (scrollTextView) {
+            if (scrollX < 0 || scrollY < 0) {
+                scrollX = mTextView.getWidth();
+                scrollY = mTextView.getLayout().getLineTop(mTextView.getLineCount()) - mTextView.getHeight();
+            }
+            Log.i(TAG, "Scrolling text view to " + scrollX + ", " + scrollY);
+            mTextView.scrollTo(scrollX, scrollY);
+        } else if (scrollScrollView) {
+            if (scrollX < 0 || scrollY < 0) {
+                Log.i(TAG, "Scrolling scroll view to bottom right");
+                mScrollView.fullScroll(View.FOCUS_DOWN);
+                mScrollView.fullScroll(View.FOCUS_RIGHT);
+            } else {
+                Log.i(TAG, "Scrolling scroll view to " + scrollX + ", " + scrollY);
+                mScrollView.scrollTo(scrollX, scrollY);
+            }
+        }
+    }
 }
diff --git a/tests/tests/assist/src/android/assist/cts/TextViewTest.java b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
new file mode 100644
index 0000000..e4390bc
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/TextViewTest.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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *  Test that the AssistStructure returned is properly formatted.
+ */
+
+public class TextViewTest extends AssistTestBase {
+    private static final String TAG = "TextViewTest";
+    private static final String TEST_CASE_TYPE = Utils.TEXTVIEW;
+
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    public TextViewTest() {
+        super();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new TextViewTestBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+    }
+
+    public void testTextView() throws Exception {
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        scrollTestApp(0, 0, true, false);
+
+        // Verify that the text view contains the right text
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistDataNullness(false, false, false, false);
+
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+                false /*FLAG_SECURE set*/);
+
+        // Verify that the scroll position of the text view is accurate after scrolling.
+        scrollTestApp(10, 50, true /* scrollTextView */, false /* scrollScrollView */);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+        scrollTestApp(-1, -1, true, false);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+        scrollTestApp(0, 0, true, true);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+        scrollTestApp(10, 50, false, true);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+    }
+
+    private class TextViewTestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+                mHasResumedLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) &&  mReadyLatch != null) {
+                mReadyLatch.countDown();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/WebViewTest.java b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
new file mode 100644
index 0000000..e367e22
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *  Test that the AssistStructure returned is properly formatted.
+ */
+
+public class WebViewTest extends AssistTestBase {
+    private static final String TAG = "WebViewTest";
+    private static final String TEST_CASE_TYPE = Utils.WEBVIEW;
+
+    private boolean mWebViewSupported;
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mTestWebViewLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    public WebViewTest() {
+        super();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new WebViewTestBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        filter.addAction(Utils.TEST_ACTIVITY_LOADED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+    }
+
+    private void waitForTestActivity() throws Exception {
+        Log.i(TAG, "waiting for webview in test activity to load");
+        if (!mTestWebViewLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            // wait for webView to load completely.
+        }
+    }
+
+    public void testWebView() throws Exception {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+            return;
+        }
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        waitForOnResume();
+        waitForTestActivity();
+        startSession();
+        waitForContext();
+        verifyAssistDataNullness(false, false, false, false);
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+                false /*FLAG_SECURE set*/);
+    }
+
+    private class WebViewTestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+                mHasResumedLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) && mReadyLatch != null) {
+                mReadyLatch.countDown();
+            } else if (action.equals(Utils.TEST_ACTIVITY_LOADED) && mTestWebViewLatch != null) {
+                mTestWebViewLatch.countDown();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/AndroidManifest.xml b/tests/tests/assist/testapp/AndroidManifest.xml
index ecfafd6..fa08f55 100644
--- a/tests/tests/assist/testapp/AndroidManifest.xml
+++ b/tests/tests/assist/testapp/AndroidManifest.xml
@@ -18,45 +18,44 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.assist.testapp">
 
+    <uses-permission android:name="android.permission.INTERNET" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
-        <activity android:name="TestApp"
-                android:label="Assist Structure Test Activity"
-                android:theme="@android:style/Theme.Material.Light">
+        <activity android:name=".TestApp"
+                android:label="Assist Structure Test Activity">
           <intent-filter>
               <action android:name="android.intent.action.TEST_APP_ASSIST_STRUCTURE" />
+              <action android:name="android.intent.action.TEST_APP_LARGE_VIEWHIERARCHY" />
               <category android:name="android.intent.category.DEFAULT" />
               <category android:name="android.intent.category.VOICE" />
           </intent-filter>
         </activity>
-        <activity android:name="DisableContextActivity"
-            android:label="Disable Context Test Activity"
-            android:theme="@android:style/Theme.Material.Light">
+        <activity android:name=".DisableContextActivity"
+            android:label="Disable Context Test Activity">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_APP_DISABLE_CONTEXT" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity android:name="SecureActivity"
-                  android:label="Secure Test Activity"
-                  android:theme="@android:style/Theme.Material.Light">
+        <activity android:name=".SecureActivity"
+                  android:label="Secure Test Activity">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_APP_FLAG_SECURE" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE" />
             </intent-filter>
         </activity>
-        <activity android:name="LifecycleActivity"
-                  android:label="Life Cycle Check Activity"
-                  android:theme="@android:style/Theme.Material.Light">
+        <activity android:name=".LifecycleActivity"
+                  android:label="Life Cycle Check Activity">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_APP_LIFECYCLE" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE" />
             </intent-filter>
         </activity>
-        <activity android:name="ScreenshotActivity"
+        <activity android:name=".ScreenshotActivity"
                   android:label="Screenshot Test Activity">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_APP_SCREENSHOT" />
@@ -64,7 +63,7 @@
                 <category android:name="android.intent.category.VOICE" />
             </intent-filter>
         </activity>
-        <activity android:name="ExtraAssistDataActivity"
+        <activity android:name=".ExtraAssistDataActivity"
             android:label="Extra Assist Test Activity">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_APP_EXTRA_ASSIST" />
@@ -72,5 +71,27 @@
                 <category android:name="android.intent.category.VOICE" />
             </intent-filter>
         </activity>
+        <activity android:name=".TextViewActivity"
+            android:label="TextView Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_TEXTVIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".WebViewActivity"
+            android:label="WebView Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_WEBVIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".FocusChangeActivity"
+            android:label="Focus Change Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_FOCUS_CHANGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/tests/assist/testapp/res/layout/multiple_text_views.xml b/tests/tests/assist/testapp/res/layout/multiple_text_views.xml
new file mode 100644
index 0000000..455d5e3
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/multiple_text_views.xml
@@ -0,0 +1,79 @@
+<?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">
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <ScrollView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1">
+    <TextView
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:scrollbars="vertical"
+      android:text="@string/text_too_large_to_fit" />
+  </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/text_view.xml b/tests/tests/assist/testapp/res/layout/text_view.xml
new file mode 100644
index 0000000..0f0f427
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/text_view.xml
@@ -0,0 +1,42 @@
+<?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">
+    <TextView
+        android:id="@+id/text_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:focusable="false"
+        android:focusableInTouchMode="false"
+        android:scrollbars="vertical"
+        android:clickable="true"
+        android:text="@string/text_too_large_to_fit" />
+
+    <ScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:scrollbars="vertical"
+            android:text="@string/text_too_large_to_fit" />
+    </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/webview.xml b/tests/tests/assist/testapp/res/layout/webview.xml
new file mode 100644
index 0000000..bdb8082
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/webview.xml
@@ -0,0 +1,25 @@
+<?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">
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    </WebView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/values/strings.xml b/tests/tests/assist/testapp/res/values/strings.xml
index ae4f16e..670024b 100644
--- a/tests/tests/assist/testapp/res/values/strings.xml
+++ b/tests/tests/assist/testapp/res/values/strings.xml
@@ -14,5 +14,271 @@
      limitations under the License.
 -->
 <resources>
+
+    <string name="app_name">Memegen</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
     <string name="welcome">Hello there!</string>
+    <string name="text_too_large_to_fit">❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € →Hello هتاف للترحيب שלום
+        përshëndetje Добры дзень 您好 হ্যালো здравей მიესალმები Χαίρετε હેલો नमस्ते Nnọọ こんにちは ಹಲೋ
+        Сәлеметсіз бе ជំរាបសួរ 안녕하세요 ສະບາຍດີ ഹലോ हॅलो Сайн байна уу नमस्ते سلامהעלא ہیلو
+        မင်္ဂလာပါ ਸਤ ਸ੍ਰੀ ਅਕਾਲ Здравствуйте здраво ආයුබෝවන් ஹலோ హలో สวัสดี Pẹlẹ o
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+        convallis nunc et vestibulum. Sed et consequat quam, blandit varius tortor. Curabitur
+        accumsan nulla lectus, placerat condimentum odio elementum vel. Nulla erat ex, accumsan ut
+        enim sagittis, scelerisque efficitur ante. Nullam quis orci nec magna maximus malesuada ac
+        id sem. Nam sagittis erat risus, a accumsan neque congue sit amet. Nullam risus velit,
+        faucibus eget scelerisque et, maximus eget arcu. Sed porta sed libero ac imperdiet.
+
+        Nulla sem lectus, ullamcorper id dui vel, rutrum interdum augue. Proin aliquam nisi vitae
+        hendrerit tempor. Mauris porttitor velit et egestas feugiat. Vivamus eu dapibus libero,
+        quis fringilla urna. Suspendisse non turpis dui. Vivamus facilisis diam vitae est auctor
+        luctus. Etiam quis lectus viverra, interdum turpis eu, aliquam sem. Nulla vulputate lacinia
+        nisi a dictum. Cras faucibus vitae tortor at ullamcorper. Quisque sit amet sapien maximus,
+        ornare nisi non, imperdiet magna. Vestibulum tempor metus ac mi ultrices dapibus.
+
+        Suspendisse potenti. Mauris pellentesque lacinia tristique. Pellentesque vel dui quis sem
+        lacinia imperdiet feugiat vitae sem. Proin a arcu magna. Sed quis augue eu mi accumsan
+        pellentesque pretium in leo. Duis euismod purus mauris, ac tempor erat auctor non. Quisque
+        bibendum est pulvinar ex dapibus, ac tincidunt nibh tempus. Mauris sodales sem id purus
+        commodo iaculis. Pellentesque a quam dapibus, vehicula lectus at, tincidunt arcu. In
+        placerat porttitor urna quis consequat. Nullam feugiat nisl sed urna hendrerit, sed
+        elementum massa iaculis. Fusce sit amet turpis hendrerit, varius lorem sed, luctus mi.
+        Phasellus sit amet ex orci. Duis scelerisque nisl quis efficitur maximus. Curabitur vitae
+        accumsan nunc, eget varius nisi.
+
+        Fusce efficitur malesuada luctus. Aliquam dapibus tortor sit amet purus semper, sit amet
+        pretium lorem feugiat. Maecenas gravida sed arcu et placerat. Nulla facilisi. Cras placerat
+        rutrum mi, in rutrum mauris maximus at. Mauris eu suscipit ante. Nullam pharetra egestas
+        diam a viverra. Donec sem turpis, tempor malesuada est vel, blandit accumsan magna. In
+        iaculis velit in efficitur hendrerit. Nulla facilisi. Curabitur eget ligula lorem. Sed sit
+        amet dolor ut ligula malesuada condimentum. Phasellus molestie augue eget libero commodo,
+        vel blandit ex blandit.
+
+        Morbi cursus tortor ante, et tempus nisi tempus et. Suspendisse quis gravida diam. Aliquam
+        efficitur dolor sit amet sollicitudin varius. Etiam libero purus, ornare nec nulla vel,
+        ullamcorper blandit nisl. Sed vel consequat diam, id placerat sem. Donec quis elementum
+        urna. In posuere bibendum nunc, in condimentum justo blandit ac. Quisque enim lorem,
+        gravida at purus at, sollicitudin imperdiet erat. Ut consectetur rutrum ante, et pretium
+        odio iaculis a. Nullam a nibh vulputate, volutpat lectus eu, pellentesque felis. Nam
+        vehicula suscipit diam nec convallis. Quisque congue maximus sem, sit amet hendrerit leo
+        tempor et.
+
+        Nam eu consequat dui. Sed semper dignissim mattis. Integer tortor eros, tempor in lectus a,
+        lobortis aliquam dolor. Phasellus at sagittis magna. Nulla eleifend orci ac urna auctor,
+        sit amet luctus urna vulputate. Nulla venenatis venenatis erat ac finibus. Etiam
+        ullamcorper elementum suscipit. Morbi nec velit non mauris porta finibus. Nullam in
+        sagittis odio. Praesent eget nisl ut mauris vestibulum feugiat. Sed vulputate at elit et
+        cursus. Praesent viverra erat blandit nunc egestas, vel feugiat ex condimentum. Class
+        aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+
+        Nulla fermentum mattis urna, non gravida eros vestibulum et. Fusce porttitor augue turpis,
+        sit amet aliquam augue sodales non. Nunc neque odio, sagittis sed gravida euismod, commodo
+        at libero. Donec porttitor pulvinar neque vitae lobortis. Aliquam accumsan velit nec sapien
+        placerat egestas. Aliquam at tincidunt massa, et dignissim justo. Donec sapien ante, rutrum
+        et tristique a, commodo a massa.
+
+        Nunc placerat lobortis magna, et molestie lacus semper porta. Lorem ipsum dolor sit amet,
+        consectetur adipiscing elit. Phasellus ac ligula dui. Duis ultrices viverra eros fermentum
+        finibus. Integer ultrices, felis in accumsan volutpat, mi ligula hendrerit nunc, nec
+        accumsan mauris tellus sit amet metus. Ut pharetra enim et sapien sollicitudin, nec
+        ultricies urna pharetra. Morbi non tortor nec dui feugiat rutrum. Aliquam malesuada sodales
+        risus, sed congue nunc accumsan vitae. Etiam nunc magna, tempus non suscipit eu, feugiat ut
+        nibh. Maecenas et libero ut nisl pellentesque tempor nec vel quam. Etiam sem ligula,
+        ullamcorper non dolor a, viverra placerat nulla. Nullam dictum commodo dui, sed ultrices
+        enim sagittis eget. Morbi non consectetur lectus. In gravida, augue vitae pulvinar
+        molestie, ligula orci vulputate ex, at bibendum urna felis nec nibh. Sed nisl nunc, iaculis
+        at augue venenatis, fringilla accumsan velit. Curabitur nec augue porttitor, rutrum nisi
+        vitae, elementum orci.
+
+        Vestibulum eu tortor iaculis, dignissim velit quis, rhoncus dolor. Donec et tincidunt
+        nulla. Duis faucibus auctor erat ac ultricies. In a fermentum mi. Fusce vitae mi id sem
+        interdum tincidunt. Nulla hendrerit orci turpis, in maximus elit mollis eget. Aliquam erat
+        volutpat. Phasellus mattis est nibh, ut scelerisque ligula egestas eu. Ut molestie orci a
+        malesuada tempor. Sed tempus arcu id orci gravida faucibus. Vivamus ac lacinia neque, at
+        vehicula magna.
+
+        Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Nullam aliquam, justo scelerisque egestas sodales, purus odio posuere arcu, ac ultrices
+        nunc felis non massa. Aliquam vulputate ipsum sed aliquet auctor. In pulvinar, eros sit
+        amet ultricies tristique, lacus ipsum scelerisque eros, nec vestibulum est lectus quis
+        lorem. Pellentesque ac augue ut eros mattis viverra vitae ut lacus. Phasellus imperdiet
+        efficitur elit eget tincidunt. Donec sodales metus at dolor pulvinar, at gravida nibh
+        facilisis. Sed nec tellus luctus, cursus lacus sed, euismod orci. Maecenas sit amet leo
+        orci. Nulla non leo non mi eleifend consequat sit amet vitae dui. Sed gravida gravida
+        justo, tincidunt ultrices justo semper vitae. Fusce at nisi nisl. Morbi molestie quis justo
+        a convallis. Curabitur massa lacus, feugiat quis mauris ac, malesuada viverra est.
+
+        Phasellus bibendum faucibus velit, ac scelerisque velit tincidunt eu. Curabitur quis
+        suscipit erat, ac feugiat odio. Nullam et sapien et nibh maximus posuere. Vivamus faucibus
+        justo eget dictum sollicitudin. Etiam at leo eget elit facilisis lobortis. Maecenas
+        bibendum tortor non erat pretium dignissim. Fusce id imperdiet augue. Suspendisse dignissim
+        magna vel odio viverra varius. Maecenas suscipit ante et lorem sodales vehicula. Quisque
+        vel magna id sem suscipit iaculis. Etiam in elementum risus.
+
+        Suspendisse odio nisi, pharetra et purus sit amet, placerat lobortis diam. Phasellus enim
+        nunc, posuere sed porta in, ornare eu ipsum. Phasellus imperdiet porta neque, vitae dapibus
+        tellus feugiat eget. Nulla sodales leo ac efficitur luctus. Vivamus eget ipsum quis ante
+        pulvinar blandit. Vestibulum a justo convallis justo elementum viverra ut sit amet nisl.
+        Suspendisse eget augue fermentum, sagittis risus a, rhoncus elit. Vestibulum in maximus
+        tortor, non vestibulum libero. Nunc accumsan neque a nisl dapibus, id laoreet neque congue.
+        Pellentesque sapien odio, fringilla non nulla nec, varius placerat diam. Aliquam
+        consectetur neque eu ipsum posuere, nec dignissim sem faucibus. Donec sit amet tempor
+        sapien. Nam at libero vel lorem dapibus ultrices a vel augue. Nunc facilisis justo ante,
+        mollis tristique velit aliquet quis. Mauris consectetur odio at urna bibendum aliquam.
+
+           Nullam lectus orci, hendrerit ut ultrices in, dapibus pellentesque nibh. Aliquam arcu
+        metus, lobortis vel dignissim id, tempus ut ante. Integer vitae ante augue. In hac habitasse
+        platea dictumst. Vestibulum in tellus ante. Cras nisi tellus, congue ac velit quis, rhoncus
+        ornare ligula. Sed facilisis gravida pellentesque. Praesent id ultrices orci, ac ultricies
+        arcu. Donec at ante quis augue faucibus congue. Donec mattis quam dui, ut vestibulum orci
+        tempor mattis. Phasellus in quam id tortor varius ullamcorper ac ac ante. Proin cursus
+        accumsan sem, vel finibus lectus eleifend ut. Donec efficitur feugiat diam id ultricies. In
+        quis euismod nisi. Vestibulum eget viverra sapien. Donec scelerisque nec elit vel viverra.
+
+           Sed mi urna, rutrum quis augue vel, aliquet placerat diam. Proin faucibus in odio et
+        consequat. Proin ut ex in mauris eleifend efficitur. Praesent ullamcorper sollicitudin urna,
+        sed mollis elit hendrerit non. Duis leo lorem, efficitur eu auctor sit amet, scelerisque
+        scelerisque magna. Mauris eget massa auctor, viverra arcu a, elementum nibh. Sed
+        pellentesque, nulla sed condimentum posuere, tortor metus congue sem, nec placerat neque
+        magna vitae purus.
+
+           Etiam at risus vitae sapien aliquam condimentum. Vestibulum id libero placerat purus
+        vehicula consectetur. Pellentesque sapien sapien, posuere at pulvinar at, ultrices sed est.
+        Maecenas nec condimentum ante. Aenean volutpat, ex condimentum hendrerit hendrerit, quam
+        nisl  pharetra nibh, vitae bibendum nisl odio vel lacus. Morbi mi tellus, bibendum id mauris
+        eu,  facilisis volutpat turpis. Maecenas rutrum convallis felis. Quisque eget feugiat felis.
+        Duis pellentesque iaculis massa ut facilisis. Donec nec commodo magna. Integer aliquet orci
+        a odio eleifend elementum. Quisque molestie, urna ut molestie eleifend, odio neque maximus
+        enim, eget viverra metus lectus eget quam. Fusce nec urna ac neque bibendum aliquam vel quis
+        dui. Fusce ac quam consequat, feugiat leo vitae, auctor felis.
+
+           Sed ac metus mauris. Sed sed velit ut tortor aliquam vestibulum at eu arcu. Etiam eu
+        posuere lacus. Maecenas id lacus quis sem mollis sodales. Quisque justo sapien, vulputate ac
+        mi ut, congue vestibulum orci. Donec euismod erat rutrum, laoreet urna sed, accumsan purus.
+        Donec eu quam a sapien condimentum accumsan. Sed at tellus lorem. Curabitur bibendum, arcu
+        sit amet finibus sodales, mi sem finibus sem, eget scelerisque tellus neque vel urna.
+        Suspendisse eu augue nec erat suscipit luctus sed non metus.
+
+           Suspendisse porttitor ex ipsum. Pellentesque tristique eros sed pharetra porttitor.
+        Quisque ut elit vehicula, aliquet est nec, faucibus tellus. Donec ex augue, congue eu
+        dignissim  maximus, vestibulum at purus. Nulla quam enim, laoreet sit amet molestie vel,
+        dapibus nec tortor. Sed interdum massa ac orci gravida, vel viverra lacus lacinia. Donec
+        nisl lacus, fermentum at faucibus ac, consequat ut nibh. Praesent laoreet est augue, vitae
+        maximus dui efficitur sit amet. Cras ipsum tellus, tincidunt at volutpat non, tincidunt ut
+        elit. Morbi commodo sagittis gravida. Pellentesque sed ante massa. Phasellus a turpis non
+        turpis cursus consequat sed nec tortor. Proin et augue elit.
+
+           Duis finibus sem commodo rutrum pulvinar. In sollicitudin ante magna, vel facilisis
+        tellus fringilla vel. Nam purus ex, tincidunt eget varius at, euismod nec elit. Curabitur
+        consequat nulla vel nisi iaculis, ut mattis odio congue. Nulla et mollis tortor, a maximus
+        justo. Donec semper eros sed nunc rhoncus condimentum. Donec nibh purus, interdum non lectus
+        id, porta convallis eros.
+
+           Sed hendrerit, dui non sagittis sollicitudin, enim ex imperdiet sapien, et maximus lorem
+        dolor a enim. Nulla risus nisl, vestibulum at ornare posuere, congue in felis. Duis sagittis
+        id diam a varius. Donec viverra eu orci sodales commodo. Cras metus tortor, sodales vitae
+        auctor non, scelerisque a ante. Quisque sodales nisi libero, ut lobortis enim suscipit ut.
+        Cras mi ipsum, maximus non bibendum sit amet, pharetra quis ipsum. Vestibulum venenatis, odi
+        at hendrerit pretium, tellus diam auctor justo, non posuere quam mauris id nisl. Nam dolor
+        nibh, molestie et lectus et, scelerisque porta elit. Vestibulum viverra condimentum auctor.
+        In eros tortor, convallis sed quam eu, ultrices malesuada purus. Integer lorem quam,
+        ultricies at est consectetur, sagittis porttitor eros. Proin non risus vitae lacus
+        consectetur malesuada non pulvinar justo. Aliquam mollis nisi nunc, sit amet vulputate metus
+        sollicitudin vel.
+
+           Quisque auctor varius fermentum. Praesent mollis justo sit amet est consectetur, in
+        volutpat tellus mollis. Aenean at bibendum eros, at finibus nibh. Phasellus nec mi sem.
+        Mauris pellentesque dui sit amet lobortis aliquam. Ut nec massa at urna aliquam gravida vel
+        in magna. Donec consectetur sapien magna, a auctor sapien placerat eu.
+
+           Pellentesque aliquet ante sed lacus gravida rutrum. Maecenas euismod varius felis, nec
+        tempus metus tempus et. Nam convallis augue a massa scelerisque, vel pharetra dolor
+        scelerisque. Fusce porttitor mi a magna rutrum condimentum. Aliquam fermentum at turpis at
+        auctor. Nulla ut suscipit dui. Donec rutrum viverra aliquam. Donec elementum nisl sapien, ac
+        blandit risus porta facilisis. Proin tellus dolor, ornare vel magna sit amet, maximus
+        volutpat felis. Aenean euismod aliquet purus, at finibus nunc elementum eu. Ut faucibus
+        ullamcorper consectetur. Aenean egestas arcu id mauris elementum, at sollicitudin est
+        congue. Sed a odio mattis, sollicitudin erat ut, tristique dolor. Aliquam luctus risus quis
+        tellus semper, a vestibulum nulla viverra.
+
+           Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
+        himenaeos. Vestibulum sit amet nisi felis. Praesent condimentum consequat lacus pulvinar
+        imperdiet. Praesent vel condimentum quam. Maecenas eu aliquet odio. Vestibulum sed nulla
+        mattis lacus porta bibendum a ac eros. Nunc porttitor sagittis laoreet. Duis porta eros at
+        congue tristique. Pellentesque quis fringilla neque, a hendrerit tellus. Pellentesque ac
+        nibh ac tellus pulvinar porttitor et in est. Integer blandit lorem libero, eu pulvinar
+        tellus posuere eget. Vivamus pretium nulla ligula, ut dapibus massa fringilla in.
+        Suspendisse consectetur sem non elit porta, id pellentesque erat dapibus. Quisque ex mi,
+        tempus et hendrerit nec, gravida quis odio. Ut at mi in leo scelerisque venenatis.
+
+           Ut sed tellus in risus tincidunt tempor ut at arcu. Maecenas ut convallis justo. In
+        rutrum urna eu massa rhoncus, eget condimentum augue vehicula. Nullam eget placerat erat.
+        Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+        Aenean at volutpat orci, a lobortis dolor. Sed consequat facilisis interdum. Fusce libero
+        neque, fringilla in congue a, vehicula rutrum ipsum. Nam ornare placerat vestibulum. Proin
+        nec orci velit. Pellentesque scelerisque gravida diam, ut tristique libero tempus eu. Nam
+        semper lacus nec nulla volutpat imperdiet non eget tortor. Sed et pellentesque ligula.
+
+           Aenean a dolor dolor. Curabitur ut placerat lacus, sit amet aliquet orci. Aliquam erat
+        volutpat. Cras mollis sit amet lectus ornare pretium. Vestibulum fringilla orci vel est
+        iaculis, at mattis quam condimentum. Vivamus semper elit consectetur lectus gravida, in
+        sollicitudin mi fringilla. Donec eget lorem in orci blandit aliquam. Pellentesque libero
+        tellus, dignissim id augue et, vulputate viverra nisl. Cum sociis natoque penatibus et
+        magnis dis parturient montes, nascetur ridiculus mus. Donec ac vulputate metus, eu suscipit
+        sem. Donec placerat, nulla at sodales hendrerit, orci tortor vestibulum purus, non pharetra
+        nulla purus posuere arcu.
+
+           Quisque feugiat elit sem, ac interdum diam pharetra nec. Curabitur sem libero, vulputate
+        eu libero vitae, volutpat facilisis ligula. Aenean maximus erat laoreet, interdum ante in,
+        ultrices nisi. Nullam nec efficitur sapien. Integer feugiat et tortor ac bibendum. Donec a
+        scelerisque tortor. Cras quis viverra diam, vitae viverra ipsum. Aliquam ultrices neque sem,
+        congue sodales elit tempus sit amet. Pellentesque habitant morbi tristique senectus et netus
+        et malesuada fames ac turpis egestas. Sed dignissim ipsum eget diam rhoncus, ut finibus
+        nulla pretium. Morbi suscipit nibh vel nisl posuere molestie. Maecenas posuere turpis et
+        rutrum consectetur. Morbi venenatis arcu non gravida vulputate. Vivamus auctor tellus
+        ullamcorper ligula vestibulum cursus. Nunc gravida sit amet nisl quis facilisis.
+
+           Praesent ut justo vestibulum, accumsan mi et, feugiat purus. Nullam pulvinar iaculis
+        pharetra. Aliquam pulvinar risus sit amet elit suscipit tincidunt. In hac habitasse platea
+        dictumst. Etiam eget velit ac magna lacinia efficitur. Vestibulum ante ipsum primis in
+        faucibus orci luctus et ultrices posuere cubilia Curae; Cras volutpat tempus sollicitudin.
+        Ut et ante elit.
+
+           Sed ac tortor justo. Fusce sed metus libero. Duis sagittis tortor ac ante sollicitudin,
+        nec efficitur nibh euismod. Donec porttitor cursus quam, in aliquam lorem feugiat ut.
+        Aliquam tempor lacus eu feugiat feugiat. Nunc pulvinar, libero at auctor commodo, metus diam
+        commodo lorem, in feugiat elit ex non ligula. Quisque at vestibulum sapien, nec facilisis
+        neque. Aenean luctus, arcu ut rhoncus luctus, est massa rhoncus mauris, nec luctus urna sem
+        quis massa. Nam elit felis, congue et ligula eget, ultricies tincidunt erat. Vivamus
+        eleifend no dui ac dictum. In nulla justo, pulvinar ut tristique sed, congue et orci.
+
+           Quisque imperdiet mi lectus, ac scelerisque augue posuere ut. Duis non pulvinar ipsum,
+        finibus risus. Donec ullamcorper nisl at sodales lobortis. Mauris neque leo, vestibulum sit
+        amet placerat vel, aliquet vel sapien. Morbi massa tellus, scelerisque quis nisl in, feugiat
+        posuere augue. Aenean congue sem ut ipsum vulputate rhoncus vitae at eros. Maecenas in velit
+        orci pellentesque lobortis ac at felis. Vestibulum odio quam, lacinia dapibus ornare eu,
+        vulputate a eros. Curabitur eleifend ornare tellus, non sollicitudin justo viverra in. Sed
+        sodales neque et lacus semper, in pharetra est consequat. Nunc vehicula volutpat lectus, sit
+        amet scelerisque nisi. Aenean sollicitudin, sem at ultricies efficitur, eros metus
+        nisl, et fringilla felis lacus non orci. Praesent eros libero, finibus in purus id,
+        tempor ipsum. Donec suscipit libero velit. Aliquam quis diam pharetra, cursus ipsum in,
+        gravida metus. Maecenas ultrices ligula a ullamcorper scelerisque.
+
+           Donec tincidunt felis turpis, id venenatis neque convallis in. Proin euismod ligula nec
+        urna vulputate, sed elementum mauris ultrices. Ut efficitur sem vel mi vestibulum placerat.
+        Sed fermentum lacus nec metus dictum, a commodo quam fermentum. Sed vel vulputate magna.
+        Integer convallis nisi sit amet mi lobortis pellentesque. In egestas porttitor elit eu
+        scelerisque. Suspendisse eleifend vel enim quis tincidunt. Sed placerat risus et pretium
+        porttitor. Nam justo mi, cursus eu pellentesque vel, bibendum ut nisl. Nulla condimentum
+        lorem, non sagittis lorem volutpat vitae. Mauris nec libero lorem. Vestibulum lacus ex,
+        vulputate non massa vitae, pellentesque vestibulum dolor.
+
+           Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Suspendisse vitae erat nisl. Vestibulum elit ante, semper et semper sit amet, fringilla
+        sapien. Morbi ac nisi sit amet turpis tincidunt mattis ac eget nisl. Nunc a venenatis quam,
+        facilisis maximus odio. Aliquam erat volutpat. Maecenas leo enim, ornare a magna quis,
+        venenatis ornare nulla. Aliquam venenatis nibh et elit tincidunt, ut egestas lorem finibus.
+        Integer iaculis dolor sed enim blandit vestibulum. Nullam vel libero ultricies, sagittis
+        tortor non, molestie eros.</string>
 </resources>
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java
new file mode 100644
index 0000000..4ab24ed
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.testapp;
+
+import android.app.Activity;
+import android.assist.common.Utils;
+import android.content.Intent;
+import android.util.Log;
+
+public class FocusChangeActivity extends Activity {
+    private static final String TAG = "FocusChangeActivity";
+    private boolean mGainedFocus = false;
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        if (hasFocus && !mGainedFocus) {
+            mGainedFocus = true;
+            Log.i(TAG, "gained focus");
+            sendBroadcast(new Intent(Utils.GAINED_FOCUS));
+        } else if (!hasFocus && mGainedFocus) {
+            Log.i(TAG, "lost focus");
+            sendBroadcast(new Intent(Utils.LOST_FOCUS));
+        }
+    }
+}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
index 4e1dc80..af10f99 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
@@ -39,22 +39,22 @@
 
     @Override
     protected void onPause() {
-        super.onPause();
         Log.i(TAG, "activity was paused");
         sendBroadcast(new Intent("android.intent.action.lifecycle_onpause"));
+        super.onPause();
     }
 
     @Override
     protected void onStop() {
-        super.onStop();
         Log.i(TAG, "activity was stopped");
         sendBroadcast(new Intent("android.intent.action.lifecycle_onstop"));
+        super.onStop();
     }
 
     @Override
     protected void onDestroy() {
-        super.onDestroy();
         Log.i(TAG, "activity was destroyed");
         sendBroadcast(new Intent("android.intent.action.lifecycle_ondestroy"));
+        super.onDestroy();
     }
 }
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
index 79bd811c..e0f83cc 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -41,11 +41,20 @@
 public class TestApp extends Activity {
     static final String TAG = "TestApp";
 
+    private String mTestCaseName;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         Log.i(TAG, "TestApp created");
-        setContentView(R.layout.test_app);
+        mTestCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+        switch (mTestCaseName) {
+            case Utils.LARGE_VIEW_HIERARCHY:
+                setContentView(R.layout.multiple_text_views);
+                return;
+            default:
+                setContentView(R.layout.test_app);
+        }
     }
 
     @Override
@@ -58,7 +67,7 @@
             @Override
             public void onGlobalLayout() {
                 layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                sendBroadcast(new Intent(Utils.ASSIST_STRUCTURE_HASRESUMED));
+                sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
             }
         });
     }
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java
new file mode 100644
index 0000000..9e57e9b
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java
@@ -0,0 +1,114 @@
+/*
+ * 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.assist.testapp;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import java.lang.Override;
+
+public class TextViewActivity extends Activity {
+    static final String TAG = "TextViewActivity";
+
+    private BroadcastReceiver mReceiver;
+    private TextView mTextView;
+    private ScrollView mScrollView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "TextViewActivity created");
+        setContentView(R.layout.text_view);
+        mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+        mTextView = (TextView) findViewById(R.id.text_view);
+        mTextView.setMovementMethod(new ScrollingMovementMethod());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.i(TAG, "TextViewActivity has resumed");
+
+        mReceiver = new ScrollReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.SCROLL_TEXTVIEW_ACTION);
+        filter.addAction(Utils.SCROLL_SCROLLVIEW_ACTION);
+        registerReceiver(mReceiver, filter);
+
+        final View layout = findViewById(android.R.id.content);
+        ViewTreeObserver vto = layout.getViewTreeObserver();
+        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+            @Override
+            public void onGlobalLayout() {
+                layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+            }
+        });
+    }
+
+    @Override
+    public void onPause() {
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver);
+        }
+        super.onPause();
+    }
+
+    class ScrollReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int scrollX, scrollY;
+            scrollX = intent.getIntExtra(Utils.SCROLL_X_POSITION, 0);
+            scrollY = intent.getIntExtra(Utils.SCROLL_Y_POSITION, 0);
+            if (intent.getAction().equals(Utils.SCROLL_TEXTVIEW_ACTION)) {
+                Log.i(TAG, "Scrolling textview to (" + scrollX + "," + scrollY + ")");
+                if (scrollX < 0 || scrollY < 0) {
+                    // Scroll to bottom as negative positions are not possible.
+                    scrollX = mTextView.getWidth();
+                    scrollY = mTextView.getLayout().getLineTop(mTextView.getLineCount())
+                            - mTextView.getHeight();
+                }
+                TextViewActivity.this.mTextView.scrollTo(scrollX, scrollY);
+            } else if (intent.getAction().equals(Utils.SCROLL_SCROLLVIEW_ACTION)) {
+                Log.i(TAG, "Scrolling scrollview to (" + scrollX + "," + scrollY + ")");
+                if (scrollX < 0 || scrollY < 0) {
+                    // Scroll to bottom
+                    TextViewActivity.this.mScrollView.fullScroll(View.FOCUS_DOWN);
+                    TextViewActivity.this.mScrollView.fullScroll(View.FOCUS_RIGHT);
+                } else {
+                    TextViewActivity.this.mScrollView.scrollTo(scrollX, scrollY);
+                }
+            }
+            Log.i(TAG, "the max height of this textview is: " + mTextView.getHeight());
+            Log.i(TAG, "the max line count of this text view is: " + mTextView.getMaxLines());
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java
new file mode 100644
index 0000000..59f96cb
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.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 android.assist.testapp;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import java.lang.Override;
+
+public class WebViewActivity extends Activity {
+    static final String TAG = "WebViewActivity";
+
+    private String mTestCaseName;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "TestApp created");
+        mTestCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+        setContentView(R.layout.webview);
+        WebView webview = (WebView) findViewById(R.id.webview);
+        webview.setWebViewClient(new WebViewClient() {
+            @Override
+            public void onPageFinished(WebView view, String url){
+                sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+            }
+        });
+        webview.loadData(Utils.WEBVIEW_HTML, "text/html", "UTF-8");
+        //webview.loadUrl(
+        //        "https://android-developers.blogspot.com/2015/08/m-developer-preview-3-final-sdk.html");
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index cc444a6..bc819c1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -41,6 +41,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -58,6 +59,7 @@
 
     private static final int SCAN_DURATION_MILLIS = 5000;
     private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 20000;
+    private CountDownLatch mFlushBatchScanLatch;
 
     private BluetoothAdapter mBluetoothAdapter;
     private BluetoothLeScanner mScanner;
@@ -215,8 +217,14 @@
                 batchScanCallback);
         sleep(SCAN_DURATION_MILLIS);
         mScanner.flushPendingScanResults(batchScanCallback);
-        sleep(1000);
+        mFlushBatchScanLatch = new CountDownLatch(1);
         List<ScanResult> results = batchScanCallback.getBatchScanResults();
+        try {
+            mFlushBatchScanLatch.await(5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            // Nothing to do.
+            Log.e(TAG, "interrupted!");
+        }
         assertTrue(!results.isEmpty());
         long scanEndMillis = SystemClock.elapsedRealtime();
         mScanner.stopScan(batchScanCallback);
@@ -252,6 +260,9 @@
             // In case onBatchScanResults are called due to buffer full, we want to collect all
             // scan results.
             mBatchScanResults.addAll(results);
+            if (mFlushBatchScanLatch != null) {
+                mFlushBatchScanLatch.countDown();
+            }
         }
 
         // Clear regular and batch scan results.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
index 75de9c0..41e2045 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
@@ -714,6 +714,7 @@
 
                     // clear out the surface and camera session
                     stopPreviewAndClearSurface(previewBuilder, rawBurstBuilder);
+                    rawReaderListener.drain();
                     closeImageReader();
                 }
             } finally {
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 c5eb27b..d78b3b5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -337,7 +337,7 @@
          *
          */
         public void drain() {
-            for (int i = 0; i < mQueue.size(); i++) {
+            while (!mQueue.isEmpty()) {
                 Image image = mQueue.poll();
                 assertNotNull("Unable to get an image", image);
                 image.close();
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 9e75d94..e0a1d4e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -69,7 +69,8 @@
     private static final long EXPOSURE_TIME_BOUNDARY_50HZ_NS = 10000000L; // 10ms
     private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8333333L; // 8.3ms, Approximation.
     private static final long EXPOSURE_TIME_ERROR_MARGIN_NS = 100000L; // 100us, Approximation.
-    private static final int SENSITIVITY_ERROR_MARGIN = 10; // 10
+    private static final float EXPOSURE_TIME_ERROR_MARGIN_RATE = 0.03f; // 3%, Approximation.
+    private static final float SENSITIVITY_ERROR_MARGIN_RATE = 0.03f; // 3%, Approximation.
     private static final int DEFAULT_NUM_EXPOSURE_TIME_STEPS = 3;
     private static final int DEFAULT_NUM_SENSITIVITY_STEPS = 16;
     private static final int DEFAULT_SENSITIVITY_STEP_SIZE = 100;
@@ -2093,10 +2094,12 @@
      */
     private void validateExposureTime(long request, long result) {
         long expTimeDelta = request - result;
+        long expTimeErrorMargin = (long)(Math.max(EXPOSURE_TIME_ERROR_MARGIN_NS, request
+                * EXPOSURE_TIME_ERROR_MARGIN_RATE));
         // First, round down not up, second, need close enough.
         mCollector.expectTrue("Exposture time is invalid for AE manaul control test, request: "
                 + request + " result: " + result,
-                expTimeDelta < EXPOSURE_TIME_ERROR_MARGIN_NS && expTimeDelta >= 0);
+                expTimeDelta < expTimeErrorMargin && expTimeDelta >= 0);
     }
 
     /**
@@ -2106,11 +2109,12 @@
      * @param result Result sensitivity
      */
     private void validateSensitivity(int request, int result) {
-        int sensitivityDelta = request - result;
+        float sensitivityDelta = (float)(request - result);
+        float sensitivityErrorMargin = request * SENSITIVITY_ERROR_MARGIN_RATE;
         // First, round down not up, second, need close enough.
         mCollector.expectTrue("Sensitivity is invalid for AE manaul control test, request: "
                 + request + " result: " + result,
-                sensitivityDelta < SENSITIVITY_ERROR_MARGIN && sensitivityDelta >= 0);
+                sensitivityDelta < sensitivityErrorMargin && sensitivityDelta >= 0);
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
index dc499ba..2ae29c3 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -316,18 +316,24 @@
                         session, previewBuilder.build(), mHandler);
 
                 // Check if all timestamps are the same
+                Image prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
                 validateTimestamps("Result 1", result.first,
-                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result.second);
+                        prevImage, result.second);
+                prevImage.close();
 
                 // Capture targeting both jpeg and preview
                 Pair<TotalCaptureResult, Long> result2 = captureAndVerifyResult(mockCaptureCallback,
                         session, multiBuilder.build(), mHandler);
 
                 // Check if all timestamps are the same
+                prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                Image jpegImage = jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
                 validateTimestamps("Result 2 Preview", result2.first,
-                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result2.second);
+                        prevImage, result2.second);
                 validateTimestamps("Result 2 Jpeg", result2.first,
-                        jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result2.second);
+                        jpegImage, result2.second);
+                prevImage.close();
+                jpegImage.close();
 
                 // Check if timestamps are increasing
                 mCollector.expectGreater("Timestamps must be increasing.", result.second,
@@ -343,10 +349,14 @@
                 long resultDiff = result4.second - result3.second;
 
                 // Check if all timestamps are the same
+                prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
                 validateTimestamps("Result 3", result3.first,
-                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result3.second);
+                        prevImage, result3.second);
+                prevImage.close();
+                prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
                 validateTimestamps("Result 4", result4.first,
-                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result4.second);
+                        prevImage, result4.second);
+                prevImage.close();
 
                 // Check that the timestamps monotonically increase at a reasonable rate
                 mCollector.expectGreaterOrEqual("Timestamps increase faster than system clock.",
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 7c80c7d..155f9dd 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -460,6 +460,10 @@
 
                             // Stop capture, delete the streams.
                             stopCapture(/*fast*/false);
+                            yuvImage.close();
+                            jpegImage.close();
+                            yuvListener.drain();
+                            jpegListener.drain();
                         } finally {
                             closeImageReader(jpegReader);
                             jpegReader = null;
@@ -645,6 +649,8 @@
                             maxYuvSz.getHeight(), ImageFormat.YUV_420_888, /*filePath*/null);
                     CameraTestUtils.validateImage(captureImage, captureSz.getWidth(),
                             captureSz.getHeight(), format, /*filePath*/null);
+                    yuvImage.close();
+                    captureImage.close();
                 }
 
                 // Stop capture, delete the streams.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
index dfba587..2795bde 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -78,7 +78,7 @@
             Exception prior = null;
 
             ImageVerifierListener yuvListener;
-            ImageReader yuvReader;
+            ImageReader yuvReader = null;
 
             try {
                 openCamera(cameraId);
@@ -102,6 +102,9 @@
                 prior = e;
             } finally {
                 try {
+                    if (yuvReader != null) {
+                        yuvReader.close();
+                    }
                     closeCamera(cameraId);
                 } catch (Exception e) {
                     if (prior != null) {
@@ -151,7 +154,7 @@
             Exception prior = null;
 
             ImageVerifierListener yuvListener;
-            ImageReader yuvReader;
+            ImageReader yuvReader = null;
 
             try {
                 openCamera(cameraId);
@@ -175,6 +178,9 @@
                 prior = e;
             } finally {
                 try {
+                    if (yuvReader != null) {
+                        yuvReader.close();
+                    }
                     closeCamera(cameraId);
                 } catch (Exception e) {
                     if (prior != null) {
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 4af88ca..39eb1dc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -66,13 +66,12 @@
     private static final int NUM_MAX_IMAGES = 4;
     private static final int NUM_RESULTS_WAIT = 30;
     private static final int[] REPROCESS_FORMATS = {ImageFormat.YUV_420_888, ImageFormat.PRIVATE};
-    private final int MAX_REPROCESS_IMAGES = 10;
+    private final int MAX_REPROCESS_IMAGES = 6;
     private final int MAX_JPEG_IMAGES = MAX_REPROCESS_IMAGES;
     private final int MAX_INPUT_IMAGES = MAX_REPROCESS_IMAGES;
     // ZSL queue depth should be bigger than the max simultaneous reprocessing capture request
     // count to maintain reasonable number of candidate image for the worse-case.
-    // Here we want to make sure we at most dequeue half of the queue max images for the worst-case.
-    private final int MAX_ZSL_IMAGES = MAX_REPROCESS_IMAGES * 2;
+    private final int MAX_ZSL_IMAGES = MAX_REPROCESS_IMAGES * 3 / 2;
     private final double REPROCESS_STALL_MARGIN = 0.1;
 
     private DeviceReportLog mReportLog;
@@ -434,7 +433,7 @@
             // Wait for reprocess output jpeg and result come back.
             reprocessResultListener.getCaptureResultForRequest(reprocessRequest,
                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
-            mJpegListener.getImage(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS);
+            mJpegListener.getImage(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS).close();
             long numFramesMaybeStalled = mZslResultListener.getTotalNumFrames();
             assertTrue("Reprocess capture result should be returned in "
                     + MAX_REPROCESS_RETURN_FRAME_COUNT + " frames",
@@ -475,6 +474,8 @@
             maxCaptureGapsMs[i] = maxTimestampGapMs;
         }
 
+        stopZslStreaming();
+
         String reprocessType = " YUV reprocessing ";
         if (reprocessInputFormat == ImageFormat.PRIVATE) {
             reprocessType = " opaque reprocessing ";
@@ -539,24 +540,34 @@
 
             // Get images
             startTimeMs = SystemClock.elapsedRealtime();
+            Image jpegImages[] = new Image[MAX_REPROCESS_IMAGES];
             for (int i = 0; i < MAX_REPROCESS_IMAGES; i++) {
-                mJpegListener.getImage(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS);
+                jpegImages[i] = mJpegListener.getImage(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS);
                 getImageLatenciesMs[i] = SystemClock.elapsedRealtime() - startTimeMs;
                 startTimeMs = SystemClock.elapsedRealtime();
             }
+            for (Image i : jpegImages) {
+                i.close();
+            }
         } else {
             // sync capture: issue reprocess request one by one, only submit next one when
             // the previous capture image is returned. This is to test the back to back capture
             // performance.
+            Image jpegImages[] = new Image[MAX_REPROCESS_IMAGES];
             for (int i = 0; i < MAX_REPROCESS_IMAGES; i++) {
                 startTimeMs = SystemClock.elapsedRealtime();
                 mWriter.queueInputImage(inputImages[i]);
                 mSession.capture(reprocessReqs[i].build(), null, null);
-                mJpegListener.getImage(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS);
+                jpegImages[i] = mJpegListener.getImage(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS);
                 getImageLatenciesMs[i] = SystemClock.elapsedRealtime() - startTimeMs;
             }
+            for (Image i : jpegImages) {
+                i.close();
+            }
         }
 
+        stopZslStreaming();
+
         String reprocessType = " YUV reprocessing ";
         if (reprocessInputFormat == ImageFormat.PRIVATE) {
             reprocessType = " opaque reprocessing ";
@@ -591,6 +602,12 @@
         mSession.setRepeatingRequest(zslBuilder.build(), mZslResultListener, mHandler);
     }
 
+    private void stopZslStreaming() throws Exception {
+        mSession.stopRepeating();
+        mSessionListener.getStateWaiter().waitForState(
+            BlockingSessionCallback.SESSION_READY, CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+    }
+
     /**
      * Wait for a certain number of frames, the images and results will be drained from the
      * listeners to make sure that next reprocessing can get matched results and images.
@@ -598,24 +615,22 @@
      * @param numFrameWait The number of frames to wait before return, 0 means that
      *      this call returns immediately after streaming on.
      */
-    private void waitForFrames(int numFrameWait) {
+    private void waitForFrames(int numFrameWait) throws Exception {
         if (numFrameWait < 0) {
             throw new IllegalArgumentException("numFrameWait " + numFrameWait +
                     " should be non-negative");
         }
 
-        if (numFrameWait == 0) {
-            // Let is stream out for a while
-            waitForNumResults(mZslResultListener, numFrameWait);
-            // Drain the pending images, to ensure that all future images have an associated
-            // capture result available.
-            mCameraZslImageListener.drain();
+        for (int i = 0; i < numFrameWait; i++) {
+            mCameraZslImageListener.getImage(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS).close();
         }
     }
 
     private void closeReaderWriters() {
+        mCameraZslImageListener.drain();
         CameraTestUtils.closeImageReader(mCameraZslReader);
         mCameraZslReader = null;
+        mJpegListener.drain();
         CameraTestUtils.closeImageReader(mJpegReader);
         mJpegReader = null;
         CameraTestUtils.closeImageWriter(mWriter);
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 52fd69f..de0e3d5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -14,6 +14,7 @@
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 import static com.android.ex.camera2.blocking.BlockingSessionCallback.*;
 
+import android.cts.util.MediaUtils;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraCaptureSession;
@@ -151,6 +152,9 @@
      * </p>
      */
     public void testRecordingFromPersistentSurface() throws Exception {
+        if (!MediaUtils.checkCodecForDomain(true /* encoder */, "video")) {
+            return; // skipped
+        }
         mPersistentSurface = MediaCodec.createPersistentInputSurface();
         assertNotNull("Failed to create persistent input surface!", mPersistentSurface);
 
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 8ccc931..ddae8eb 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -1351,8 +1351,8 @@
         Log.i(TAG, String.format("Testing Camera %s, config %s",
                         cameraId, MaxStreamSizes.configToString(config)));
 
-        // 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;
+        // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
+        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
         final int MIN_RESULT_COUNT = 3;
 
         // Set up outputs
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
index 68efef0..a2ddb4d 100755
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -105,7 +105,7 @@
             if (supports64Bit) {
                 assertMinMemoryMb(1824);
             } else {
-                assertMinMemoryMb(1344);
+                assertMinMemoryMb(1099);
             }
         } else if (greaterThanDpi(density, DENSITY_400, screenSize,
                 SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
index c807e03..22f092a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
@@ -102,13 +102,13 @@
                 sensor.getMaximumRange() >= maxRange);
         double actualMinFrequency = SensorCtsHelper.getFrequency(sensor.getMaxDelay(),
                 TimeUnit.MICROSECONDS);
-        assertTrue(String.format("%s Min Frequency actual=%.2f expected=%dHz",
+        assertTrue(String.format("%s Min Frequency actual=%.2f expected=%.2fHz",
                     sensor.getName(), actualMinFrequency, minFrequency), actualMinFrequency <=
                 minFrequency + 0.1);
 
         double actualMaxFrequency = SensorCtsHelper.getFrequency(sensor.getMinDelay(),
                 TimeUnit.MICROSECONDS);
-        assertTrue(String.format("%s Max Frequency actual=%.2f expected=%dHz",
+        assertTrue(String.format("%s Max Frequency actual=%.2f expected=%.2fHz",
                     sensor.getName(), actualMaxFrequency, maxFrequency), actualMaxFrequency >=
                 maxFrequency - 0.1);
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 2c3c6f4..cffafdc 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -88,7 +88,7 @@
     }
 
     @Override
-    protected void tearDown(){
+    protected void tearDown() {
         if (mSensorManager != null) {
             // SensorManager will check listener and status, so just unregister listener
             mSensorManager.unregisterListener(mNullSensorEventListener);
@@ -256,7 +256,8 @@
         if (mTriggerSensor == null) {
             throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
-        boolean  result = mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+        boolean  result =
+            mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
         assertFalse(result);
     }
 
@@ -265,7 +266,8 @@
         if (mTriggerSensor == null) {
             throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
-        boolean result = mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+        boolean result =
+            mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
         assertFalse(result);
     }
 
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 55465ac..9cb3710 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -38,21 +38,32 @@
     private SensorCtsHelper() {}
 
     /**
-     * Get the value of the 95th percentile using nearest rank algorithm.
+     * Get percentiles using nearest rank algorithm.
      *
-     * @throws IllegalArgumentException if the collection is null or empty
+     * @param percentiles List of percentiles interested. Its range is 0 to 1, instead of in %.
+     *        The value will be internally bounded.
+     *
+     * @throws IllegalArgumentException if the collection or percentiles is null or empty
      */
-    public static <TValue extends Comparable<? super TValue>> TValue get95PercentileValue(
-            Collection<TValue> collection) {
+    public static <TValue extends Comparable<? super TValue>> List<TValue> getPercentileValue(
+            Collection<TValue> collection, float[] percentiles) {
         validateCollection(collection);
+        if(percentiles == null || percentiles.length == 0) {
+            throw new IllegalStateException("percentiles cannot be null or empty");
+        }
 
         List<TValue> arrayCopy = new ArrayList<TValue>(collection);
         Collections.sort(arrayCopy);
 
-        // zero-based array index
-        int arrayIndex = (int) Math.round(arrayCopy.size() * 0.95 + .5) - 1;
-
-        return arrayCopy.get(arrayIndex);
+        List<TValue> percentileValues = new ArrayList<TValue>();
+        for (float p : percentiles) {
+            // zero-based array index
+            int arrayIndex = (int) Math.round(arrayCopy.size() * p - .5f);
+            // bound the index to avoid out of range error
+            arrayIndex = Math.min(Math.max(arrayIndex, 0), arrayCopy.size() - 1);
+            percentileValues.add(arrayCopy.get(arrayIndex));
+        }
+        return percentileValues;
     }
 
     /**
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 3e70e75..e8df1ab 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -172,8 +172,7 @@
      */
     public List<TestSensorEvent> getCollectedEvents() {
         synchronized (mCollectedEvents){
-            List<TestSensorEvent> collectedEventsList = (List)mCollectedEvents.clone();
-            return Collections.unmodifiableList(collectedEventsList);
+            return Collections.unmodifiableList((List<TestSensorEvent>) mCollectedEvents.clone());
         }
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
index d246ec5..9d36f37 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
@@ -16,8 +16,6 @@
 
 package android.hardware.cts.helpers.sensorverification;
 
-import junit.framework.Assert;
-
 import android.content.Context;
 import android.content.pm.PackageManager;
 
@@ -34,6 +32,7 @@
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import junit.framework.Assert;
 
 /**
  * A {@link ISensorVerification} which verifies that the sensor jitter is in an acceptable range.
@@ -43,15 +42,22 @@
 
     // sensorType: threshold (% of expected period)
     private static final SparseIntArray DEFAULTS = new SparseIntArray(12);
-    // Max allowed jitter (in percentage).
+    // Max allowed jitter in +/- sense (in percentage).
     private static final int GRACE_FACTOR = 2;
     private static final int THRESHOLD_PERCENT_FOR_HIFI_SENSORS = 1 * GRACE_FACTOR;
+
+    // Margin sample intervals that considered outliers, lower and higher margin is discarded
+    // before verification
+    private static final float OUTLIER_MARGIN = 0.025f; //2.5%
+
     static {
         // Use a method so that the @deprecation warning can be set for that method only
         setDefaults();
     }
 
-    private final int mThresholdAsPercentage;
+    private final float     mOutlierMargin;
+    private final long      mThresholdNs;
+    private final long      mExpectedPeriodNs; // for error message only
     private final List<Long> mTimestamps = new LinkedList<Long>();
 
     /**
@@ -59,8 +65,10 @@
      *
      * @param thresholdAsPercentage the acceptable margin of error as a percentage
      */
-    public JitterVerification(int thresholdAsPercentage) {
-        mThresholdAsPercentage = thresholdAsPercentage;
+    public JitterVerification(float outlierMargin, long thresholdNs, long expectedPeriodNs) {
+        mExpectedPeriodNs = expectedPeriodNs;
+        mOutlierMargin = outlierMargin;
+        mThresholdNs = thresholdNs;
     }
 
     /**
@@ -71,16 +79,20 @@
      */
     public static JitterVerification getDefault(TestSensorEnvironment environment) {
         int sensorType = environment.getSensor().getType();
-        int threshold = DEFAULTS.get(sensorType, -1);
-        if (threshold == -1) {
+
+        int thresholdPercent = DEFAULTS.get(sensorType, -1);
+        if (thresholdPercent == -1) {
             return null;
         }
         boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_HIFI_SENSORS);
         if (hasHifiSensors) {
-           threshold = THRESHOLD_PERCENT_FOR_HIFI_SENSORS;
+           thresholdPercent = THRESHOLD_PERCENT_FOR_HIFI_SENSORS;
         }
-        return new JitterVerification(threshold);
+
+        long expectedPeriodNs = (long) environment.getExpectedSamplingPeriodUs() * 1000;
+        long jitterThresholdNs = expectedPeriodNs * thresholdPercent * 2 / 100; // *2 is for +/-
+        return new JitterVerification(OUTLIER_MARGIN, jitterThresholdNs, expectedPeriodNs);
     }
 
     /**
@@ -99,24 +111,33 @@
             return;
         }
 
-        List<Double> jitters = getJitterValues();
-        double jitter95PercentileNs = SensorCtsHelper.get95PercentileValue(jitters);
-        long firstTimestamp = mTimestamps.get(0);
-        long lastTimestamp = mTimestamps.get(timestampsCount - 1);
-        long measuredPeriodNs = (lastTimestamp - firstTimestamp) / (timestampsCount - 1);
-        double jitter95PercentilePercent = (jitter95PercentileNs * 100.0) / measuredPeriodNs;
-        stats.addValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY, jitter95PercentilePercent);
+        List<Long> deltas = getDeltaValues();
+        float percentiles[] = new float[2];
+        percentiles[0] = mOutlierMargin;
+        percentiles[1] = 1 - percentiles[0];
 
-        boolean success = (jitter95PercentilePercent < mThresholdAsPercentage);
+        List<Long> percentileValues = SensorCtsHelper.getPercentileValue(deltas, percentiles);
+        double normalizedRange =
+                (double)(percentileValues.get(1) - percentileValues.get(0)) / mThresholdNs;
+
+        double percentageJitter =
+                (double)(percentileValues.get(1) - percentileValues.get(0)) /
+                        mExpectedPeriodNs / 2 * 100; //one side variation comparing to sample time
+
+        stats.addValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY, percentageJitter);
+
+        boolean success = normalizedRange <= 1.0;
         stats.addValue(PASSED_KEY, success);
 
         if (!success) {
             String message = String.format(
-                    "Jitter out of range: measured period=%dns, jitter(95th percentile)=%.2f%%"
-                            + " (expected < %d%%)",
-                    measuredPeriodNs,
-                    jitter95PercentilePercent,
-                    mThresholdAsPercentage);
+                    "Jitter out of range: requested period = %dns, " +
+                    "jitter min, max, range (95th percentile) = (%dns, %dns, %dns), " +
+                    "jitter expected range <= %dns",
+                    mExpectedPeriodNs,
+                    percentileValues.get(0), percentileValues.get(1),
+                    percentileValues.get(1) - percentileValues.get(0),
+                    mThresholdNs);
             Assert.fail(message);
         }
     }
@@ -126,7 +147,7 @@
      */
     @Override
     public JitterVerification clone() {
-        return new JitterVerification(mThresholdAsPercentage);
+        return new JitterVerification(mOutlierMargin, mThresholdNs, mExpectedPeriodNs);
     }
 
     /**
@@ -138,19 +159,14 @@
     }
 
     /**
-     * Get the list of all jitter values. Exposed for unit testing.
+     * Get the list of delta values. Exposed for unit testing.
      */
-    List<Double> getJitterValues() {
+    List<Long> getDeltaValues() {
         List<Long> deltas = new ArrayList<Long>(mTimestamps.size() - 1);
         for (int i = 1; i < mTimestamps.size(); i++) {
             deltas.add(mTimestamps.get(i) - mTimestamps.get(i - 1));
         }
-        double deltaMean = StatisticsUtils.getMean(deltas);
-        List<Double> jitters = new ArrayList<Double>(deltas.size());
-        for (long delta : deltas) {
-            jitters.add(Math.abs(delta - deltaMean));
-        }
-        return jitters;
+        return deltas;
     }
 
     @SuppressWarnings("deprecation")
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 50e288c..d8e1586 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
@@ -27,11 +27,10 @@
 import java.util.List;
 
 /**
- * Tests for {@link EventOrderingVerification}.
+ * Tests for {@link JitterVerification}.
  */
 public class JitterVerificationTest extends TestCase {
 
-
     public void testVerify() {
         final int SAMPLE_SIZE = 100;
         // for unit testing the verification, only the parameter 'sensorMightHaveMoreListeners' is
@@ -67,54 +66,58 @@
         } catch (AssertionError e) {
             // Expected;
         }
-        verifyStats(stats, false, 47.34);
+        verifyStats(stats, false, 25); // 500 us range (250 us in single-sided sense)
+                                       // divide by 1ms requested sample time x 100%
     }
 
-    public void testCalculateJitter() {
+    public void testCalculateDelta() {
         long[] timestamps = new long[]{0, 1, 2, 3, 4};
         JitterVerification verification = getVerification(1, timestamps);
-        List<Double> jitterValues = verification.getJitterValues();
-        assertEquals(4, jitterValues.size());
-        assertEquals(0.0, jitterValues.get(0));
-        assertEquals(0.0, jitterValues.get(1));
-        assertEquals(0.0, jitterValues.get(2));
-        assertEquals(0.0, jitterValues.get(3));
+        List<Long> deltaValues = verification.getDeltaValues();
+        assertEquals(4, deltaValues.size());
+        assertEquals(1, deltaValues.get(0).longValue());
+        assertEquals(1, deltaValues.get(1).longValue());
+        assertEquals(1, deltaValues.get(2).longValue());
+        assertEquals(1, deltaValues.get(3).longValue());
 
         timestamps = new long[]{0, 0, 2, 4, 4};
         verification = getVerification(1, timestamps);
-        jitterValues = verification.getJitterValues();
-        assertEquals(4, jitterValues.size());
-        assertEquals(1.0, jitterValues.get(0));
-        assertEquals(1.0, jitterValues.get(1));
-        assertEquals(1.0, jitterValues.get(2));
-        assertEquals(1.0, jitterValues.get(3));
+        deltaValues = verification.getDeltaValues();
+        assertEquals(4, deltaValues.size());
+        assertEquals(0, deltaValues.get(0).longValue());
+        assertEquals(2, deltaValues.get(1).longValue());
+        assertEquals(2, deltaValues.get(2).longValue());
+        assertEquals(0, deltaValues.get(3).longValue());
 
         timestamps = new long[]{0, 1, 4, 9, 16};
         verification = getVerification(1, timestamps);
-        jitterValues = verification.getJitterValues();
-        assertEquals(4, jitterValues.size());
-        assertEquals(4, jitterValues.size());
-        assertEquals(3.0, jitterValues.get(0));
-        assertEquals(1.0, jitterValues.get(1));
-        assertEquals(1.0, jitterValues.get(2));
-        assertEquals(3.0, jitterValues.get(3));
+        deltaValues = verification.getDeltaValues();
+        assertEquals(4, deltaValues.size());
+        assertEquals(1, deltaValues.get(0).longValue());
+        assertEquals(3, deltaValues.get(1).longValue());
+        assertEquals(5, deltaValues.get(2).longValue());
+        assertEquals(7, deltaValues.get(3).longValue());
     }
 
-    private static JitterVerification getVerification(int threshold, long ... timestamps) {
+    private static JitterVerification getVerification(int marginPercent, long ... timestamps) {
         Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
         for (long timestamp : timestamps) {
             events.add(new TestSensorEvent(null, timestamp, 0, null));
         }
-        JitterVerification verification = new JitterVerification(threshold);
+        long samplePeriodNs = 1000*1000; //1000Hz
+        long jitterThresholdNs = 20*1000; // 2% of that
+
+        JitterVerification verification =
+                new JitterVerification(marginPercent/100.0f, jitterThresholdNs, samplePeriodNs);
         verification.addSensorEvents(events);
         return verification;
     }
 
-    private void verifyStats(SensorStats stats, boolean passed, double jitter95) {
+    private void verifyStats(SensorStats stats, boolean passed, double percentageJitter) {
         assertEquals(passed, stats.getValue(JitterVerification.PASSED_KEY));
         assertEquals(
-                jitter95,
+                percentageJitter,
                 (Double) stats.getValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY),
-                0.1);
+                0.01);
     }
 }
diff --git a/tests/tests/media/res/raw/heap_oob_flac.mp3 b/tests/tests/media/res/raw/heap_oob_flac.mp3
new file mode 100644
index 0000000..ae542d0
--- /dev/null
+++ b/tests/tests/media/res/raw/heap_oob_flac.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4 b/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4
new file mode 100644
index 0000000..110c0d6
--- /dev/null
+++ b/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 4c03183..9ae6b64 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1646,6 +1646,7 @@
                     Thread.sleep(WAIT_MSEC); // wait for the data to drain.
                     // -------- tear down --------------
                     track.release();
+                    Thread.sleep(WAIT_MSEC); // wait for release to complete
                     frequency += 50; // increment test tone frequency
                 }
             }
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 9da229c..8650d20 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -20,6 +20,8 @@
 import android.content.res.AssetFileDescriptor;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
@@ -1141,4 +1143,87 @@
     private static String getMimeTypeFor(MediaFormat format) {
         return format.getString(MediaFormat.KEY_MIME);
     }
+
+    /**
+     * Returns the first codec capable of encoding the specified MIME type, or null if no match was
+     * found.
+     */
+    private static MediaCodecInfo selectCodec(String mimeType) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+            if (!codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; j++) {
+                if (types[j].equalsIgnoreCase(mimeType)) {
+                    return codecInfo;
+                }
+            }
+        }
+        return null;
+    }
+
+  /**
+   * Checks whether the given resolution is supported by the AVC codec.
+   */
+    private static boolean isAvcSupportedSize(int width, int height) {
+        MediaCodecInfo mediaCodecInfo = selectCodec(OUTPUT_VIDEO_MIME_TYPE);
+        CodecCapabilities cap = mediaCodecInfo.getCapabilitiesForType(OUTPUT_VIDEO_MIME_TYPE);
+        if (cap == null) { // not supported
+            return false;
+        }
+        int highestLevel = 0;
+        for (CodecProfileLevel lvl : cap.profileLevels) {
+            if (lvl.level > highestLevel) {
+                highestLevel = lvl.level;
+            }
+        }
+        int maxW = 0;
+        int maxH = 0;
+        int bitRate = 0;
+        int fps = 0; // frame rate for the max resolution
+        switch(highestLevel) {
+            // Do not support Level 1 to 2.
+            case CodecProfileLevel.AVCLevel1:
+            case CodecProfileLevel.AVCLevel11:
+            case CodecProfileLevel.AVCLevel12:
+            case CodecProfileLevel.AVCLevel13:
+            case CodecProfileLevel.AVCLevel1b:
+            case CodecProfileLevel.AVCLevel2:
+                return false;
+            case CodecProfileLevel.AVCLevel21:
+                maxW = 352;
+                maxH = 576;
+                break;
+            case CodecProfileLevel.AVCLevel22:
+                maxW = 720;
+                maxH = 480;
+                break;
+            case CodecProfileLevel.AVCLevel3:
+                maxW = 720;
+                maxH = 480;
+                break;
+            case CodecProfileLevel.AVCLevel31:
+                maxW = 1280;
+                maxH = 720;
+                break;
+            case CodecProfileLevel.AVCLevel32:
+                maxW = 1280;
+                maxH = 720;
+                break;
+            case CodecProfileLevel.AVCLevel4: // only try up to 1080p
+            default:
+                maxW = 1920;
+                maxH = 1080;
+                break;
+        }
+        if(maxW*maxH < width*height)
+            return false;
+        else
+            return true;
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/JetPlayerTest.java b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
index fc03bcc..4df3555 100644
--- a/tests/tests/media/src/android/media/cts/JetPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
@@ -55,6 +55,10 @@
 
     @Override
     protected void tearDown() throws Exception {
+        // Prevent tests from failing with EAS_ERROR_FILE_ALREADY_OPEN
+        // after a previous test fails.
+        mJetPlayer.closeJetFile();
+
         File jetFile = new File(mJetFile);
         if (jetFile.exists()) {
             jetFile.delete();
@@ -63,31 +67,32 @@
     }
 
     public void testLoadJetFromPath() throws Throwable {
-        mJetPlayer.clearQueue();
+        assertTrue(mJetPlayer.clearQueue());
         prepareFile();
         mJetPlayer.setEventListener(mOnJetEventListener);
-        mJetPlayer.loadJetFile(mJetFile);
+        assertTrue(mJetPlayer.loadJetFile(mJetFile));
         runJet();
     }
 
     public void testLoadJetFromFd() throws Throwable {
-        mJetPlayer.clearQueue();
+        assertTrue(mJetPlayer.clearQueue());
         mJetPlayer.setEventListener(mOnJetEventListener, mHandler);
-        mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet));
+        assertTrue(mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet)));
         runJet();
     }
 
     public void testQueueJetSegmentMuteArray() throws Throwable {
-        mJetPlayer.clearQueue();
+        assertTrue(mJetPlayer.clearQueue());
         mJetPlayer.setEventListener(mOnJetEventListener, mHandler);
-        mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet));
+        assertTrue(mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet)));
         byte userID = 0;
         int segmentNum = 3;
         int libNum = -1;
         int repeatCount = 0;
         int transpose = 0;
         boolean[] muteFlags = new boolean[32];
-        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum, repeatCount, transpose,
+        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum,
+                repeatCount, transpose,
                 muteFlags, userID));
         assertTrue(mJetPlayer.play());
         for (int i = 0; i < muteFlags.length; i++) {
@@ -96,7 +101,8 @@
         muteFlags[8] = false;
         muteFlags[9] = false;
         muteFlags[10] = false;
-        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum, repeatCount, transpose,
+        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum,
+                repeatCount, transpose,
                 muteFlags, userID));
         Thread.sleep(20000);
         assertTrue(mJetPlayer.pause());
@@ -112,16 +118,19 @@
         int repeatCount = 1;
         int transpose = 0;
         int muteFlags = 0;
-        mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+        assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+                transpose, muteFlags, userID));
 
         segmentNum = 6;
         repeatCount = 1;
         transpose = -1;
-        mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+        assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+                transpose, muteFlags, userID));
 
         segmentNum = 7;
         transpose = 0;
-        mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+        assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+                transpose, muteFlags, userID));
 
         for (int i = 0; i < 7; i++) {
             assertTrue(mJetPlayer.triggerClip(i));
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 813af0f..71cbd61 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -600,6 +600,29 @@
         return actualMax;
     }
 
+    private boolean knownTypes(String type) {
+        return (type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC  ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC3      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB   ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB   ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM    ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS   ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2    ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4    ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9      ));
+    }
+
     public void testGetMaxSupportedInstances() {
         final int MAX_INSTANCES = 32;
         StringBuilder xmlOverrides = new StringBuilder();
@@ -610,6 +633,10 @@
 
             String[] types = info.getSupportedTypes();
             for (int j = 0; j < types.length; ++j) {
+                if (!knownTypes(types[j])) {
+                    Log.d(TAG, "skipping unknown type " + types[j]);
+                    continue;
+                }
                 Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
                 CodecCapabilities caps = info.getCapabilitiesForType(types[j]);
                 int max = caps.getMaxSupportedInstances();
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 73aa4f4..75a5a13 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.cts.util.MediaUtils;
+import android.hardware.Camera;
 import android.media.AudioManager;
 import android.media.MediaCodec;
 import android.media.MediaDataSource;
@@ -49,6 +50,7 @@
 import java.io.File;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.UUID;
 import java.util.Vector;
@@ -95,6 +97,49 @@
         }
     }
 
+    public void testonInputBufferFilledSigsegv() throws Exception {
+        testIfMediaServerDied(R.raw.on_input_buffer_filled_sigsegv);
+    }
+
+    public void testFlacHeapOverflow() throws Exception {
+        testIfMediaServerDied(R.raw.heap_oob_flac);
+    }
+
+    private void testIfMediaServerDied(int res) throws Exception {
+        mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+            @Override
+            public boolean onError(MediaPlayer mp, int what, int extra) {
+                assertTrue(mp == mMediaPlayer);
+                assertTrue("mediaserver process died", what != MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+                Log.w(LOG_TAG, "onError " + what);
+                return false;
+            }
+        });
+
+        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                assertTrue(mp == mMediaPlayer);
+                mOnCompletionCalled.signal();
+            }
+        });
+
+        AssetFileDescriptor afd = mResources.openRawResourceFd(res);
+        mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        afd.close();
+        try {
+            mMediaPlayer.prepare();
+            mMediaPlayer.start();
+            if (!mOnCompletionCalled.waitForSignal(5000)) {
+                Log.w(LOG_TAG, "testIfMediaServerDied: Timed out waiting for Error/Completion");
+            }
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "playback failed", e);
+        } finally {
+            mMediaPlayer.release();
+        }
+    }
+
     // Bug 13652927
     public void testVorbisCrash() throws Exception {
         MediaPlayer mp = mMediaPlayer;
@@ -700,7 +745,7 @@
     public void testVideoSurfaceResetting() throws Exception {
         final int tolerance = 150;
         final int audioLatencyTolerance = 1000;  /* covers audio path latency variability */
-        final int seekPos = 5000;
+        final int seekPos = 4760;  // This is the I-frame position
 
         final CountDownLatch seekDone = new CountDownLatch(1);
 
@@ -722,7 +767,12 @@
         mMediaPlayer.setDisplay(getActivity().getSurfaceHolder2());
         int posAfter = mMediaPlayer.getCurrentPosition();
 
-        assertEquals(posAfter, posBefore, tolerance);
+        /* temporarily disable timestamp checking because MediaPlayer now seeks to I-frame
+         * position, instead of requested position. setDisplay invovles a seek operation
+         * internally.
+         */
+        // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+        // assertEquals(posAfter, posBefore, tolerance);
         assertTrue(mMediaPlayer.isPlaying());
 
         Thread.sleep(SLEEP_TIME);
@@ -736,7 +786,8 @@
         posBefore = mMediaPlayer.getCurrentPosition();
         mMediaPlayer.setDisplay(null);
         posAfter = mMediaPlayer.getCurrentPosition();
-        assertEquals(posAfter, posBefore, tolerance);
+        // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+        // assertEquals(posAfter, posBefore, tolerance);
         assertTrue(mMediaPlayer.isPlaying());
 
         Thread.sleep(SLEEP_TIME);
@@ -745,7 +796,8 @@
         mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
         posAfter = mMediaPlayer.getCurrentPosition();
 
-        assertEquals(posAfter, posBefore, tolerance);
+        // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+        // assertEquals(posAfter, posBefore, tolerance);
         assertTrue(mMediaPlayer.isPlaying());
 
         Thread.sleep(SLEEP_TIME);
@@ -771,15 +823,34 @@
         return getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
     }
 
+    private Camera mCamera;
     private void testRecordedVideoPlaybackWithAngle(int angle) throws Exception {
-        final int width = RECORDED_VIDEO_WIDTH;
-        final int height = RECORDED_VIDEO_HEIGHT;
+        int width = RECORDED_VIDEO_WIDTH;
+        int height = RECORDED_VIDEO_HEIGHT;
         final String file = RECORDED_FILE;
         final long durationMs = RECORDED_DURATION_MS;
 
         if (!hasCamera()) {
             return;
         }
+
+        boolean isSupported = false;
+        mCamera = Camera.open(0);
+        Camera.Parameters parameters = mCamera.getParameters();
+        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+        for (Camera.Size size : previewSizes)
+        {
+            if (size.width == width && size.height == height) {
+                isSupported = true;
+                break;
+            }
+        }
+        mCamera.release();
+        mCamera = null;
+        if (!isSupported) {
+            width = previewSizes.get(0).width;
+            height = previewSizes.get(0).height;
+        }
         checkOrientation(angle);
         recordVideo(width, height, angle, file, durationMs);
         checkDisplayedVideoSize(width, height, angle, file);
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index f7b6c91..4c90e56 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -60,6 +60,8 @@
     private static final int RECORDED_DUR_TOLERANCE_MS = 1000;
     private static final int VIDEO_WIDTH = 176;
     private static final int VIDEO_HEIGHT = 144;
+    private static int mVideoWidth = VIDEO_WIDTH;
+    private static int mVideoHeight = VIDEO_HEIGHT;
     private static final int VIDEO_BIT_RATE_IN_BPS = 128000;
     private static final double VIDEO_TIMELAPSE_CAPTURE_RATE_FPS = 1.0;
     private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
@@ -219,6 +221,7 @@
         int durMs = timelapse? RECORD_TIME_LAPSE_MS: RECORD_TIME_MS;
         for (int cameraId = 0; cameraId < nCamera; cameraId++) {
             mCamera = Camera.open(cameraId);
+            setSupportedResolution(mCamera);
             recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse);
             mCamera.release();
             mCamera = null;
@@ -226,6 +229,21 @@
         }
     }
 
+    private void setSupportedResolution(Camera camera) {
+        Camera.Parameters parameters = camera.getParameters();
+        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+        for (Camera.Size size : previewSizes)
+        {
+            if (size.width == VIDEO_WIDTH && size.height == VIDEO_HEIGHT) {
+                mVideoWidth = VIDEO_WIDTH;
+                mVideoHeight = VIDEO_HEIGHT;
+                return;
+            }
+        }
+        mVideoWidth = previewSizes.get(0).width;
+        mVideoHeight = previewSizes.get(0).height;
+    }
+
     private void recordVideoUsingCamera(
             Camera camera, String fileName, int durMs, boolean timelapse) throws Exception {
         // FIXME:
@@ -242,7 +260,7 @@
         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
         mMediaRecorder.setVideoFrameRate(frameRate);
-        mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
+        mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
         mMediaRecorder.setOutputFile(fileName);
         mMediaRecorder.setLocation(LATITUDE, LONGITUDE);
@@ -872,6 +890,12 @@
         boolean success = false;
         Surface surface = null;
         int noOfFailure = 0;
+
+        if (!hasH264()) {
+            MediaUtils.skipTest("no codecs");
+            return true;
+        }
+
         try {
             if (persistent) {
                 surface = MediaCodec.createPersistentInputSurface();
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index e3d2f09..fb1521d 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -174,6 +174,7 @@
         private boolean mSignaledDecoderEOS;
 
         protected boolean mCompleted;
+        protected boolean mEncoderIsActive;
         protected boolean mEncodeOutputFormatUpdated;
         protected final Object mCondition = new Object();
 
@@ -322,6 +323,10 @@
                         mCompleted = true;
                         mCondition.notifyAll(); // condition is always satisfied
                     }
+                } else {
+                    synchronized(mCondition) {
+                        mEncoderIsActive = true;
+                    }
                 }
             }
         }
@@ -395,6 +400,11 @@
                             break;
                         }
                         if (!haveBuffers()) {
+                            if (mEncoderIsActive) {
+                                mEncoderIsActive = false;
+                                Log.d(TAG, "No more input but still getting output from encoder.");
+                                continue;
+                            }
                             fail("timed out after " + mBuffersToRender.size()
                                     + " decoder output and " + mEncInputBuffers.size()
                                     + " encoder input buffers");
@@ -557,7 +567,6 @@
             implements SurfaceTexture.OnFrameAvailableListener {
         private static final String TAG = "SurfaceVideoProcessor";
         private boolean mFrameAvailable;
-        private boolean mEncoderIsActive;
         private boolean mGotDecoderEOS;
         private boolean mSignaledEncoderEOS;
 
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index 88dbd7c..9a99c22 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,6 +16,9 @@
 
 package android.net.cts;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -23,6 +26,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
@@ -467,4 +471,23 @@
             mAvailableLatch.countDown();
         }
     }
+
+    /** Verify restricted networks cannot be requested. */
+    public void testRestrictedNetworks() {
+        // Verify we can request unrestricted networks:
+        NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET).build();
+        NetworkCallback callback = new NetworkCallback();
+        mCm.requestNetwork(request, callback);
+        mCm.unregisterNetworkCallback(callback);
+        // Verify we cannot request restricted networks:
+        request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build();
+        callback = new NetworkCallback();
+        try {
+            mCm.requestNetwork(request, callback);
+            fail("No exception thrown when restricted network requested.");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+    }
 }
diff --git a/tests/tests/os/jni/seccomp_sample_program.cpp b/tests/tests/os/jni/seccomp_sample_program.cpp
index 3c90196..3bc7da4 100644
--- a/tests/tests/os/jni/seccomp_sample_program.cpp
+++ b/tests/tests/os/jni/seccomp_sample_program.cpp
@@ -826,7 +826,7 @@
   {0x35, 0, 4, 0x76},
   {0x35, 0, 2, 0x79},
   {0x35, 0, 241, 0x7a},
-  {0x35, 240, 239, 0x7b},
+  {0x35, 240, 239, 0x7c},
   {0x35, 238, 239, 0x77},
   {0x35, 0, 2, 0x72},
   {0x35, 0, 236, 0x73},
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 419f320..af60139 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.1", "5.1.1"));
-    private static final int EXPECTED_SDK = 22;
+            new HashSet<String>(Arrays.asList("6.0"));
+    private static final int EXPECTED_SDK = 23;
     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/CpuFeaturesTest.java b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
index ecdb399..d8e128b 100644
--- a/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
+++ b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
@@ -83,7 +83,7 @@
 
     private static final String[] armv8RequiredFeatures = {
             "half", "thumb", "fastmult", "vfp", "edsp", "neon",
-            "vfpv3", "tlsi", "vfpv4", "idiva", "idivt" };
+            "vfpv3", "vfpv4", "idiva", "idivt" };
 
     private static void assertInCpuinfo(List<String> features,
             String feature) {
diff --git a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
index f7e5443..c260706 100644
--- a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
@@ -98,4 +98,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
index e75ec94..1378bdb 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -140,6 +140,11 @@
 
     @Override
     public void setUp() throws Exception {
+        super.setUp();
+        if (!supportsPrinting()) {
+            return;
+        }
+
         // Make sure we start with a clean slate.
         clearPrintSpoolerData();
         enablePrintServices();
@@ -176,23 +181,26 @@
 
     @Override
     public void tearDown() throws Exception {
-        // Done with the activity.
-        getActivity().finish();
-        enableImes();
+        if (supportsPrinting()) {
+            // Done with the activity.
+            getActivity().finish();
+            enableImes();
 
-        // Restore the locale if needed.
-        if (mOldLocale != null) {
-            Resources resources = getInstrumentation().getTargetContext().getResources();
-            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
-            Configuration newConfiguration = new Configuration(resources.getConfiguration());
-            newConfiguration.locale = mOldLocale;
-            mOldLocale = null;
-            resources.updateConfiguration(newConfiguration, displayMetrics);
+            // Restore the locale if needed.
+            if (mOldLocale != null) {
+                Resources resources = getInstrumentation().getTargetContext().getResources();
+                DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+                Configuration newConfiguration = new Configuration(resources.getConfiguration());
+                newConfiguration.locale = mOldLocale;
+                mOldLocale = null;
+                resources.updateConfiguration(newConfiguration, displayMetrics);
+            }
+
+            disablePrintServices();
+            // Make sure the spooler is cleaned.
+            clearPrintSpoolerData();
         }
-
-        disablePrintServices();
-        // Make sure the spooler is cleaned.
-        clearPrintSpoolerData();
+        super.tearDown();
     }
 
     protected void print(final PrintDocumentAdapter adapter) {
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 25bc6ac..d8d8dfa 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index 2d55fb6..e61d787 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -31,7 +31,10 @@
 		android_security_cts_SELinuxTest.cpp \
 		android_security_cts_MMapExecutableTest.cpp \
 		android_security_cts_AudioPolicyBinderTest.cpp \
-		android_security_cts_EncryptionTest.cpp
+		android_security_cts_EncryptionTest.cpp \
+		android_security_cts_MediaPlayerInfoLeakTest.cpp \
+		android_security_cts_AudioEffectBinderTest.cpp \
+		android_security_cts_AudioFlingerBinderTest.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index c60b866..2f749b7 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -25,7 +25,10 @@
 extern int register_android_security_cts_SELinuxTest(JNIEnv*);
 extern int register_android_security_cts_MMapExecutableTest(JNIEnv* env);
 extern int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env);
+extern int register_android_security_cts_AudioFlingerBinderTest(JNIEnv* env);
 extern int register_android_security_cts_EncryptionTest(JNIEnv* env);
+extern int register_android_security_cts_MediaPlayerInfoLeakTest(JNIEnv* env);
+extern int register_android_security_cts_AudioEffectBinderTest(JNIEnv* env);
 
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env = NULL;
@@ -70,5 +73,17 @@
         return JNI_ERR;
     }
 
+    if (register_android_security_cts_MediaPlayerInfoLeakTest(env)) {
+        return JNI_ERR;
+    }
+
+    if (register_android_security_cts_AudioEffectBinderTest(env)) {
+        return JNI_ERR;
+    }
+
+    if (register_android_security_cts_AudioFlingerBinderTest(env)) {
+        return JNI_ERR;
+    }
+
     return JNI_VERSION_1_4;
 }
diff --git a/tests/tests/security/jni/android_security_cts_AudioEffectBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioEffectBinderTest.cpp
new file mode 100644
index 0000000..4c27416
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_AudioEffectBinderTest.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioEffectBinderTest-JNI"
+
+#include <jni.h>
+#include <media/AudioEffect.h>
+#include <media/IEffect.h>
+
+using namespace android;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java
+ */
+
+struct EffectClient : public BnEffectClient {
+    EffectClient() { }
+    virtual void controlStatusChanged(bool controlGranted __unused) { }
+    virtual void enableStatusChanged(bool enabled __unused) { }
+    virtual void commandExecuted(uint32_t cmdCode __unused,
+            uint32_t cmdSize __unused,
+            void *pCmdData __unused,
+            uint32_t replySize __unused,
+            void *pReplyData __unused) { }
+};
+
+struct DeathRecipient : public IBinder::DeathRecipient {
+    DeathRecipient() : mDied(false) { }
+    virtual void binderDied(const wp<IBinder>& who __unused) { mDied = true; }
+    bool died() const { return mDied; }
+    bool mDied;
+};
+
+static bool isIEffectCommandSecure(IEffect *effect)
+{
+    // some magic constants here
+    const int COMMAND_SIZE = 1024 + 12; // different than reply size to get different heap frag
+    char cmdData[COMMAND_SIZE];
+    memset(cmdData, 0xde, sizeof(cmdData));
+
+    const int REPLY_DATA_SIZE = 256;
+    char replyData[REPLY_DATA_SIZE];
+    bool secure = true;
+    for (int k = 0; k < 10; ++k) {
+        Parcel data;
+        data.writeInterfaceToken(effect->getInterfaceDescriptor());
+        data.writeInt32(0);  // 0 is EFFECT_CMD_INIT
+        data.writeInt32(sizeof(cmdData));
+        data.write(cmdData, sizeof(cmdData));
+        data.writeInt32(sizeof(replyData));
+
+        Parcel reply;
+        status_t status = effect->asBinder(effect)->transact(3, data, &reply);  // 3 is COMMAND
+        ALOGV("transact status: %d", status);
+        if (status != NO_ERROR) {
+            ALOGW("invalid transaction status %d", status);
+            continue;
+        }
+
+        ALOGV("reply data avail %zu", reply.dataAvail());
+        status = reply.readInt32();
+        ALOGV("reply status %d", status);
+        if (status == NO_ERROR) {
+            continue;
+        }
+
+        int size = reply.readInt32();
+        ALOGV("reply size %d", size);
+        if (size != sizeof(replyData)) { // we expect 0 or full reply data if command failed
+            ALOGW_IF(size != 0, "invalid reply size: %d", size);
+            continue;
+        }
+
+        // Note that if reply.read() returns success, it should completely fill replyData.
+        status = reply.read(replyData, sizeof(replyData));
+        if (status != NO_ERROR) {
+            ALOGW("invalid reply read - ignoring");
+            continue;
+        }
+        unsigned int *out = (unsigned int *)replyData;
+        for (size_t index = 0; index < sizeof(replyData) / sizeof(*out); ++index) {
+            if (out[index] != 0) {
+                secure = false;
+                ALOGI("leaked data = %#08x", out[index]);
+            }
+        }
+    }
+    ALOGI("secure: %s", secure ? "YES" : "NO");
+    return secure;
+}
+
+static jboolean android_security_cts_AudioEffect_test_isCommandSecure()
+{
+    const sp<IAudioFlinger> &audioFlinger = AudioSystem::get_audio_flinger();
+    if (audioFlinger.get() == NULL) {
+        ALOGE("could not get audioflinger");
+        return JNI_FALSE;
+    }
+
+    static const effect_uuid_t EFFECT_UIID_EQUALIZER =  // type
+        { 0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }};
+    sp<EffectClient> effectClient(new EffectClient());
+    effect_descriptor_t descriptor;
+    memset(&descriptor, 0, sizeof(descriptor));
+    descriptor.type = EFFECT_UIID_EQUALIZER;
+    descriptor.uuid = *EFFECT_UUID_NULL;
+    const int32_t priority = 0;
+    const int sessionId = AUDIO_SESSION_OUTPUT_MIX;
+    const audio_io_handle_t io = AUDIO_IO_HANDLE_NONE;
+    const String16 opPackageName("Exploitable");
+    status_t status;
+    int32_t id;
+    int enabled;
+    sp<IEffect> effect = audioFlinger->createEffect(&descriptor, effectClient,
+            priority, io, sessionId, opPackageName, &status, &id, &enabled);
+    if (effect.get() == NULL || status != NO_ERROR) {
+        ALOGW("could not create effect");
+        return JNI_TRUE;
+    }
+
+    sp<DeathRecipient> deathRecipient(new DeathRecipient());
+    IInterface::asBinder(effect)->linkToDeath(deathRecipient);
+
+    // check exploit
+    if (!isIEffectCommandSecure(effect.get())) {
+        ALOGE("not secure!");
+        return JNI_FALSE;
+    }
+
+    sleep(1); // wait to check death
+    if (deathRecipient->died()) {
+        ALOGE("effect binder died");
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+int register_android_security_cts_AudioEffectBinderTest(JNIEnv *env)
+{
+    static JNINativeMethod methods[] = {
+        { "native_test_isCommandSecure", "()Z",
+                (void *) android_security_cts_AudioEffect_test_isCommandSecure },
+    };
+
+    jclass clazz = env->FindClass("android/security/cts/AudioEffectBinderTest");
+    return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
+}
diff --git a/tests/tests/security/jni/android_security_cts_AudioFlingerBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioFlingerBinderTest.cpp
new file mode 100644
index 0000000..fb80d6b
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_AudioFlingerBinderTest.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioFlingerBinderTest-JNI"
+
+#include <jni.h>
+#include <binder/IServiceManager.h>
+#include <media/IAudioFlinger.h>
+#include <media/AudioSystem.h>
+#include <system/audio.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+using namespace android;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java
+ */
+
+#define TEST_ARRAY_SIZE 10000
+#define MAX_ARRAY_SIZE 1024
+#define TEST_PATTERN 0x55
+
+class MyDeathClient: public IBinder::DeathRecipient
+{
+public:
+    MyDeathClient() :
+        mAfIsDead(false) {
+    }
+
+    bool afIsDead() const { return mAfIsDead; }
+
+    // DeathRecipient
+    virtual void binderDied(const wp<IBinder>& who __unused) { mAfIsDead = true; }
+
+private:
+    bool mAfIsDead;
+};
+
+
+static bool connectAudioFlinger(sp<IAudioFlinger>& af, sp<MyDeathClient> &dr)
+{
+    int64_t startTime = 0;
+    while (af == 0) {
+        sp<IBinder> binder = defaultServiceManager()->checkService(String16("media.audio_flinger"));
+        if (binder == 0) {
+            if (startTime == 0) {
+                startTime = uptimeMillis();
+            } else if ((uptimeMillis()-startTime) > 10000) {
+                ALOGE("timeout while getting audio flinger service");
+                return false;
+            }
+            sleep(1);
+        } else {
+            af = interface_cast<IAudioFlinger>(binder);
+            dr = new MyDeathClient();
+            binder->linkToDeath(dr);
+        }
+    }
+    return true;
+}
+
+/*
+ * Checks that AudioSystem::setMasterMute() does not crash mediaserver if a duplicated output
+ * is opened.
+ */
+jboolean android_security_cts_AudioFlinger_test_setMasterMute(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioFlinger> af;
+    sp<MyDeathClient> dr;
+
+    if (!connectAudioFlinger(af, dr)) {
+        return false;
+    }
+
+    // force opening of a duplicating output
+    status_t status = AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+                                          AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                                          "0", "");
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    bool mute;
+    status = AudioSystem::getMasterMute(&mute);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    AudioSystem::setMasterMute(!mute);
+
+    sleep(1);
+
+    // Check that mediaserver did not crash
+    if (dr->afIsDead()) {
+        return false;
+    }
+
+    AudioSystem::setMasterMute(mute);
+
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+                                          AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                                          "0", "");
+
+    AudioSystem::setMasterMute(false);
+
+    return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_setMasterVolume(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioFlinger> af;
+    sp<MyDeathClient> dr;
+
+    if (!connectAudioFlinger(af, dr)) {
+        return false;
+    }
+
+    // force opening of a duplicating output
+    status_t status = AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+                                          AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                                          "0", "");
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    float vol;
+    status = AudioSystem::getMasterVolume(&vol);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    AudioSystem::setMasterVolume(vol < 0.5 ? 1.0 : 0.0);
+
+    sleep(1);
+
+    // Check that mediaserver did not crash
+    if (dr->afIsDead()) {
+        return false;
+    }
+
+    AudioSystem::setMasterMute(vol);
+
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+                                          AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                                          "0", "");
+
+    return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_listAudioPorts(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioFlinger> af;
+    sp<MyDeathClient> dr;
+
+    if (!connectAudioFlinger(af, dr)) {
+        return false;
+    }
+
+    unsigned int num_ports = TEST_ARRAY_SIZE;
+    struct audio_port *ports =
+            (struct audio_port *)calloc(TEST_ARRAY_SIZE, sizeof(struct audio_port));
+
+    memset(ports, TEST_PATTERN, TEST_ARRAY_SIZE * sizeof(struct audio_port));
+
+    status_t status = af->listAudioPorts(&num_ports, ports);
+
+    sleep(1);
+
+    // Check that the memory content above the max allowed array size was not changed
+    char *ptr = (char *)(ports + MAX_ARRAY_SIZE);
+    for (size_t i = 0; i < TEST_ARRAY_SIZE - MAX_ARRAY_SIZE; i++) {
+        if (ptr[i * sizeof(struct audio_port)] != TEST_PATTERN) {
+            free(ports);
+            return false;
+        }
+    }
+
+    free(ports);
+
+    // Check that mediaserver did not crash
+    if (dr->afIsDead()) {
+        return false;
+    }
+
+    return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_listAudioPatches(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioFlinger> af;
+    sp<MyDeathClient> dr;
+
+    if (!connectAudioFlinger(af, dr)) {
+        return false;
+    }
+
+    unsigned int num_patches = TEST_ARRAY_SIZE;
+    struct audio_patch *patches =
+            (struct audio_patch *)calloc(TEST_ARRAY_SIZE, sizeof(struct audio_patch));
+
+    memset(patches, TEST_PATTERN, TEST_ARRAY_SIZE * sizeof(struct audio_patch));
+
+    status_t status = af->listAudioPatches(&num_patches, patches);
+
+    sleep(1);
+
+    // Check that the memory content above the max allowed array size was not changed
+    char *ptr = (char *)(patches + MAX_ARRAY_SIZE);
+    for (size_t i = 0; i < TEST_ARRAY_SIZE - MAX_ARRAY_SIZE; i++) {
+        if (ptr[i * sizeof(struct audio_patch)] != TEST_PATTERN) {
+            free(patches);
+            return false;
+        }
+    }
+
+    free(patches);
+
+    // Check that mediaserver did not crash
+    if (dr->afIsDead()) {
+        return false;
+    }
+
+    return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_createEffect(JNIEnv* env __unused,
+                                                             jobject thiz __unused)
+{
+    sp<IAudioFlinger> af;
+    sp<MyDeathClient> dr;
+
+    if (!connectAudioFlinger(af, dr)) {
+        return false;
+    }
+
+    for (int j = 0; j < 10; ++j) {
+        Parcel data, reply;
+        data.writeInterfaceToken(af->getInterfaceDescriptor());
+        data.writeInt32((int32_t)j);
+        status_t status = af->asBinder(af)->transact(40, data, &reply); // 40 is CREATE_EFFECT
+        if (status != NO_ERROR) {
+            return false;
+        }
+
+        status = (status_t)reply.readInt32();
+        if (status == NO_ERROR) {
+            continue;
+        }
+
+        int id = reply.readInt32();
+        int enabled = reply.readInt32();
+        sp<IEffect> effect = interface_cast<IEffect>(reply.readStrongBinder());
+        effect_descriptor_t desc;
+        effect_descriptor_t descTarget;
+        memset(&desc, 0, sizeof(effect_descriptor_t));
+        memset(&descTarget, 0, sizeof(effect_descriptor_t));
+        reply.read(&desc, sizeof(effect_descriptor_t));
+        if (id != 0 || enabled != 0 || memcmp(&desc, &descTarget, sizeof(effect_descriptor_t))) {
+            return false;
+        }
+    }
+
+    sleep(1);
+
+    // Check that mediaserver did not crash
+    if (dr->afIsDead()) {
+        return false;
+    }
+
+    return true;
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "native_test_setMasterMute", "()Z",
+            (void *) android_security_cts_AudioFlinger_test_setMasterMute },
+    {  "native_test_setMasterVolume", "()Z",
+            (void *) android_security_cts_AudioFlinger_test_setMasterVolume },
+    {  "native_test_listAudioPorts", "()Z",
+            (void *) android_security_cts_AudioFlinger_test_listAudioPorts },
+    {  "native_test_listAudioPatches", "()Z",
+            (void *) android_security_cts_AudioFlinger_test_listAudioPatches },
+    {  "native_test_createEffect", "()Z",
+            (void *) android_security_cts_AudioFlinger_test_createEffect },
+};
+
+int register_android_security_cts_AudioFlingerBinderTest(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/security/cts/AudioFlingerBinderTest");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
index 6807523..6011920 100644
--- a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
@@ -18,6 +18,7 @@
 
 #include <jni.h>
 #include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
 #include <media/IAudioPolicyService.h>
 #include <media/AudioSystem.h>
 #include <system/audio.h>
@@ -151,12 +152,12 @@
         return false;
     }
 
-    status_t status = aps->isStreamActive((audio_stream_type_t)(-1), 0);
-    if (status == NO_ERROR) {
+    bool status = aps->isStreamActive((audio_stream_type_t)(-1), 0);
+    if (status) {
         return false;
     }
     status = aps->isStreamActive((audio_stream_type_t)AUDIO_STREAM_CNT, 0);
-    if (status == NO_ERROR) {
+    if (status) {
         return false;
     }
     return true;
@@ -186,6 +187,64 @@
     return true;
 }
 
+jint android_security_cts_AudioPolicy_test_getStreamVolumeLeak(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioPolicyService> aps;
+
+    if (!init(aps, NULL, NULL)) {
+        return -1;
+    }
+
+    // Keep synchronized with IAudioPolicyService.cpp!
+    enum {
+        GET_STREAM_VOLUME = 17,
+    };
+
+    Parcel data, reply;
+    status_t err;
+    data.writeInterfaceToken(aps->getInterfaceDescriptor());
+    data.writeInt32(-1); // stream type
+    data.writeInt32(-1); // device
+    IInterface::asBinder(aps)->transact(GET_STREAM_VOLUME, data, &reply);
+    int index = reply.readInt32();
+    err = reply.readInt32();
+
+    return index;
+}
+
+jboolean android_security_cts_AudioPolicy_test_startAudioSource(JNIEnv* env __unused,
+                                                                jobject thiz __unused)
+{
+    sp<IAudioPolicyService> aps;
+
+    if (!init(aps, NULL, NULL)) {
+        return false;
+    }
+
+    // Keep synchronized with IAudioPolicyService.cpp!
+    enum {
+        START_AUDIO_SOURCE = 41,
+    };
+
+    for (int i = 0; i < 10; ++i) {
+        Parcel data, reply;
+        data.writeInterfaceToken(aps->getInterfaceDescriptor());
+        data.writeInt32(-i);
+        IInterface::asBinder(aps)->transact(START_AUDIO_SOURCE, data, &reply);
+        status_t err = (status_t)reply.readInt32();
+        if (err == NO_ERROR) {
+            continue;
+        }
+        audio_io_handle_t handle = (audio_io_handle_t)reply.readInt32();
+        if (handle != 0) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 static JNINativeMethod gMethods[] = {
     {  "native_test_startOutput", "()Z",
             (void *) android_security_cts_AudioPolicy_test_startOutput },
@@ -195,6 +254,10 @@
                 (void *) android_security_cts_AudioPolicy_test_isStreamActive },
     {  "native_test_isStreamActiveRemotely", "()Z",
                 (void *) android_security_cts_AudioPolicy_test_isStreamActiveRemotely },
+    {  "native_test_getStreamVolumeLeak", "()I",
+                (void *) android_security_cts_AudioPolicy_test_getStreamVolumeLeak },
+    {  "native_test_startAudioSource", "()Z",
+                (void *) android_security_cts_AudioPolicy_test_startAudioSource },
 };
 
 int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env)
diff --git a/tests/tests/security/jni/android_security_cts_MediaPlayerInfoLeakTest.cpp b/tests/tests/security/jni/android_security_cts_MediaPlayerInfoLeakTest.cpp
new file mode 100644
index 0000000..0c61f25
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_MediaPlayerInfoLeakTest.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MediaPlayerInfoLeakTest-JNI"
+
+#include <jni.h>
+
+#include <binder/Parcel.h>
+#include <binder/IServiceManager.h>
+
+#include <media/IMediaPlayer.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IMediaPlayerClient.h>
+
+#include <sys/stat.h>
+
+using namespace android;
+
+static status_t connectMediaPlayer(sp<IMediaPlayer>& iMP)
+{
+   sp<IServiceManager> sm = defaultServiceManager();
+   sp<IBinder> mediaPlayerService = sm->checkService(String16("media.player"));
+
+   sp<IMediaPlayerService> iMPService = IMediaPlayerService::asInterface(mediaPlayerService);
+   sp<IMediaPlayerClient> client;
+   Parcel data, reply;
+   int dummyAudioSessionId = 1;
+   data.writeInterfaceToken(iMPService->getInterfaceDescriptor());
+   data.writeStrongBinder(IInterface::asBinder(client));
+   data.writeInt32(dummyAudioSessionId);
+
+   // Keep synchronized with IMediaPlayerService.cpp!
+    enum {
+        CREATE = IBinder::FIRST_CALL_TRANSACTION,
+    };
+   status_t err = IInterface::asBinder(iMPService)->transact(CREATE, data, &reply);
+   if (err == NO_ERROR) {
+       iMP = interface_cast<IMediaPlayer>(reply.readStrongBinder());
+   }
+   return err;
+}
+
+int testMediaPlayerInfoLeak(int command)
+{
+    sp<IMediaPlayer> iMP;
+    if (NO_ERROR != connectMediaPlayer(iMP)) {
+        return false;
+    }
+
+
+    Parcel data, reply;
+    data.writeInterfaceToken(iMP->getInterfaceDescriptor());
+    IInterface::asBinder(iMP)->transact(command, data, &reply);
+
+    int leak = reply.readInt32();
+    status_t err = reply.readInt32();
+    return  leak;
+}
+
+jint android_security_cts_MediaPlayer_test_getCurrentPositionLeak(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+  // Keep synchronized with IMediaPlayer.cpp!
+  enum {
+      GET_CURRENT_POSITION = 16,
+  };
+  return testMediaPlayerInfoLeak(GET_CURRENT_POSITION);
+}
+
+jint android_security_cts_MediaPlayer_test_getDurationLeak(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+  // Keep synchronized with IMediaPlayer.cpp!
+  enum {
+      GET_DURATION = 17,
+  };
+  return testMediaPlayerInfoLeak(GET_DURATION);
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "native_test_getCurrentPositionLeak", "()I",
+            (void *) android_security_cts_MediaPlayer_test_getCurrentPositionLeak },
+    {  "native_test_getDurationLeak", "()I",
+            (void *) android_security_cts_MediaPlayer_test_getDurationLeak },
+};
+
+int register_android_security_cts_MediaPlayerInfoLeakTest(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/security/cts/MediaPlayerInfoLeakTest");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java b/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java
new file mode 100644
index 0000000..20d9615
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.security.cts;
+
+import android.media.audiofx.AudioEffect;
+
+import java.util.UUID;
+
+import junit.framework.TestCase;
+
+public class AudioEffectBinderTest extends TestCase {
+
+    static {
+        System.loadLibrary("ctssecurity_jni");
+    }
+
+    /**
+     * Checks that IEffect::command() cannot leak data.
+     */
+    public void test_isCommandSecure() throws Exception {
+        if (isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+            assertTrue(native_test_isCommandSecure());
+        }
+    }
+
+    /* see AudioEffect.isEffectTypeAvailable(), implements hidden function */
+    private static boolean isEffectTypeAvailable(UUID type) {
+        AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+        if (desc == null) {
+            return false;
+        }
+
+        for (int i = 0; i < desc.length; i++) {
+            if (desc[i].type.equals(type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static native boolean native_test_isCommandSecure();
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java b/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java
new file mode 100644
index 0000000..202bd65
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.security.cts;
+
+import junit.framework.TestCase;
+
+public class AudioFlingerBinderTest extends TestCase {
+
+    static {
+        System.loadLibrary("ctssecurity_jni");
+    }
+
+    /**
+     * Checks that AudioSystem::setMasterMute() does not crash mediaserver if a duplicated output
+     * is opened.
+     */
+    public void test_setMasterMute() throws Exception {
+        assertTrue(native_test_setMasterMute());
+    }
+
+    /**
+     * Checks that AudioSystem::setMasterVolume() does not crash mediaserver if a duplicated output
+     * is opened.
+     */
+    public void test_setMasterVolume() throws Exception {
+        assertTrue(native_test_setMasterVolume());
+    }
+
+    /**
+     * Checks that IAudioFlinger::listAudioPorts() does not cause a memory overflow when passed a
+     * large number of ports.
+     */
+    public void test_listAudioPorts() throws Exception {
+        assertTrue(native_test_listAudioPorts());
+    }
+
+    /**
+     * Checks that IAudioFlinger::listAudioPatches() does not cause a memory overflow when passed a
+     * large number of ports.
+     */
+    public void test_listAudioPatches() throws Exception {
+        assertTrue(native_test_listAudioPatches());
+    }
+
+    /**
+     * Checks that IAudioFlinger::createEffect() does not leak information on the server side.
+     */
+    public void test_createEffect() throws Exception {
+        assertTrue(native_test_createEffect());
+    }
+
+    private static native boolean native_test_setMasterMute();
+    private static native boolean native_test_setMasterVolume();
+    private static native boolean native_test_listAudioPorts();
+    private static native boolean native_test_listAudioPatches();
+    private static native boolean native_test_createEffect();
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
index b307247..82346a1 100644
--- a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
+++ b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
@@ -56,8 +56,27 @@
         assertTrue(native_test_isStreamActiveRemotely());
     }
 
+    /**
+     * Checks that IAudioPolicyService::getStreamVolumeIndex() does not leak information
+     * when called with an invalid stream/device type.
+     */
+    public void test_getStreamVolumeLeak() throws Exception {
+        int volume = native_test_getStreamVolumeLeak();
+        assertTrue(String.format("Leaked volume 0x%08X", volume), volume == 0);
+    }
+
+    /**
+     * Checks that IAudioPolicyService::startAudioSource() cannot leak information from
+     * server side.
+     */
+    public void test_startAudioSource() throws Exception {
+        assertTrue(native_test_startAudioSource());
+    }
+
     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();
+    private static native int native_test_getStreamVolumeLeak();
+    private static native boolean native_test_startAudioSource();
 }
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
index d188aab..19866de 100644
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
@@ -26,7 +26,8 @@
 import android.security.cts.activity.ISecureRandomService;
 import android.security.cts.activity.SecureRandomService;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.cts.util.TimeoutReq;
 
 import java.io.BufferedReader;
 import java.io.EOFException;
@@ -37,7 +38,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-@LargeTest
 public class ClonedSecureRandomTest extends AndroidTestCase {
     private static final int MAX_SHUTDOWN_TRIES = 50;
 
@@ -93,6 +93,7 @@
      * sees two newly started processes with the same PID and compares their
      * output.
      */
+    @TimeoutReq(minutes=15)
     public void testCheckForDuplicateOutput() throws Exception {
         assertEquals("Only supports up to " + MAX_PID + " because of memory requirements",
                 Integer.toString(MAX_PID), getFirstLineFromFile("/proc/sys/kernel/pid_max"));
diff --git a/tests/tests/security/src/android/security/cts/ListeningPortsTest.java b/tests/tests/security/src/android/security/cts/ListeningPortsTest.java
index 87e957d..76c53c1 100644
--- a/tests/tests/security/src/android/security/cts/ListeningPortsTest.java
+++ b/tests/tests/security/src/android/security/cts/ListeningPortsTest.java
@@ -43,9 +43,22 @@
 
     static {
         // IPv4 exceptions
-        EXCEPTION_PATTERNS.add("0.0.0.0:5555");   // emulator port
-        EXCEPTION_PATTERNS.add("10.0.2.15:5555"); // net forwarding for emulator
-        EXCEPTION_PATTERNS.add("127.0.0.1:5037"); // adb daemon "smart sockets"
+        // Patterns containing ":" are allowed address port combinations
+        // Pattterns contains " " are allowed address UID combinations
+        // Patterns containing both are allowed address, port, and UID combinations
+        EXCEPTION_PATTERNS.add("0.0.0.0:5555");     // emulator port
+        EXCEPTION_PATTERNS.add("0.0.0.0:9101");     // verified ports
+        EXCEPTION_PATTERNS.add("0.0.0.0:9551");     // verified ports
+        EXCEPTION_PATTERNS.add("0.0.0.0:9552");     // verified ports
+        EXCEPTION_PATTERNS.add("10.0.2.15:5555");   // net forwarding for emulator
+        EXCEPTION_PATTERNS.add("127.0.0.1:5037");   // adb daemon "smart sockets"
+        EXCEPTION_PATTERNS.add("0.0.0.0 1020");     // used by the cast receiver
+        EXCEPTION_PATTERNS.add("0.0.0.0 10000");    // used by the cast receiver
+        EXCEPTION_PATTERNS.add("127.0.0.1 10000");  // used by the cast receiver
+        EXCEPTION_PATTERNS.add(":: 1002");          // used by remote control
+        EXCEPTION_PATTERNS.add(":: 1020");          // used by remote control
+        //no current patterns involve address, port and UID combinations
+        //Example for when necessary: EXCEPTION_PATTERNS.add("0.0.0.0:5555 10000")
     }
 
     /**
@@ -185,9 +198,11 @@
         List<ParsedProcEntry> entries = ParsedProcEntry.parse(procFilePath);
         for (ParsedProcEntry entry : entries) {
             String addrPort = entry.localAddress.getHostAddress() + ':' + entry.port;
+            String addrUid = entry.localAddress.getHostAddress() + ' ' + entry.uid;
+            String addrPortUid = addrPort + ' ' + entry.uid;
 
             if (isPortListening(entry.state, isTcp)
-                && !isException(addrPort)
+                && !(isException(addrPort) || isException(addrUid) || isException(addrPortUid))
                 && (!entry.localAddress.isLoopbackAddress() ^ loopback)) {
                 errors += "\nFound port listening on addr="
                         + entry.localAddress.getHostAddress() + ", port="
diff --git a/tests/tests/security/src/android/security/cts/MediaPlayerInfoLeakTest.java b/tests/tests/security/src/android/security/cts/MediaPlayerInfoLeakTest.java
new file mode 100644
index 0000000..e34fc05
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/MediaPlayerInfoLeakTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.security.cts;
+
+import junit.framework.TestCase;
+
+public class MediaPlayerInfoLeakTest extends TestCase {
+
+    static {
+        System.loadLibrary("ctssecurity_jni");
+    }
+
+
+    /**
+     * Checks that IMediaPlayer::getCurrentPosition() does not leak info in error case
+     */
+    public void test_getCurrentPositionLeak() throws Exception {
+        int pos = native_test_getCurrentPositionLeak();
+        assertTrue(String.format("Leaked pos 0x%08X", pos), pos == 0);
+    }
+
+    /**
+     * Checks that IMediaPlayer::getDuration() does not leak info in error case
+     */
+    public void test_getDurationLeak() throws Exception {
+        int dur = native_test_getDurationLeak();
+        assertTrue(String.format("Leaked dur 0x%08X", dur), dur == 0);
+    }
+
+    private static native int native_test_getCurrentPositionLeak();
+    private static native int native_test_getDurationLeak();
+}
diff --git a/tests/tests/security/src/android/security/cts/PutOverflowTest.java b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
new file mode 100644
index 0000000..e0eb34c
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.security.cts;
+
+import android.test.AndroidTestCase;
+import java.lang.reflect.Method;
+
+public class PutOverflowTest extends AndroidTestCase {
+    public void testCrash() throws Exception {
+        try {
+            Class<?> keystoreClass = Class.forName("android.security.KeyStore");
+            Method getInstance = keystoreClass.getMethod("getInstance");
+            Method put = keystoreClass.getMethod("put",
+                    String.class, byte[].class, int.class, int.class);
+            Object keystore = getInstance.invoke(null);
+            byte[] buffer = new byte[65536];
+            Boolean result = (Boolean)put.invoke(keystore, "crashFile", buffer, -1, 0);
+            assertTrue("Fix for ANDROID-22802399 not present", result);
+        } catch (ReflectiveOperationException ignored) {
+            // Since this test requires reflection avoid causing undue failures if classes or
+            // methods were changed.
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
index 00cbfd8..fdc3058 100644
--- a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
+++ b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
@@ -17,6 +17,7 @@
 package android.security.cts;
 
 import android.os.IBinder;
+import android.os.DeadObjectException;
 import android.os.TransactionTooLargeException;
 import android.test.AndroidTestCase;
 import android.util.Log;
@@ -108,7 +109,7 @@
                     // probably not checking for DUMP.
                     throw e;
                 }
-            } catch (TransactionTooLargeException e) {
+            } catch (TransactionTooLargeException | DeadObjectException e) {
                 // SELinux likely prevented the dump - assume safe
                 continue;
             } finally {
diff --git a/tests/tests/netlegacy22/Android.mk b/tests/tests/systemui/Android.mk
similarity index 63%
rename from tests/tests/netlegacy22/Android.mk
rename to tests/tests/systemui/Android.mk
index 3174652..1a15fd2 100644
--- a/tests/tests/netlegacy22/Android.mk
+++ b/tests/tests/systemui/Android.mk
@@ -12,5 +12,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Build the API tests and the permissions tests using their own makefiles.
-include $(call all-subdir-makefiles)
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSystemUiTestCases
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml
new file mode 100644
index 0000000..bf5df5b
--- /dev/null
+++ b/tests/tests/systemui/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.systemui">
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application>
+        <activity android:name=".LightStatusBarActivity"
+                android:theme="@android:style/Theme.Material.NoActionBar"></activity>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.systemui">
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
new file mode 100644
index 0000000..626a179
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
@@ -0,0 +1,68 @@
+/*
+ * 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.systemui;
+
+/**
+ * Copies of non-public {@link android.graphics.Color} APIs
+ */
+public class ColorUtils {
+
+    public static float brightness(int argb) {
+        int r = (argb >> 16) & 0xFF;
+        int g = (argb >> 8) & 0xFF;
+        int b = argb & 0xFF;
+
+        int V = Math.max(b, Math.max(r, g));
+
+        return (V / 255.f);
+    }
+
+    public static float hue(int argb) {
+        int r = (argb >> 16) & 0xFF;
+        int g = (argb >> 8) & 0xFF;
+        int b = argb & 0xFF;
+
+        int V = Math.max(b, Math.max(r, g));
+        int temp = Math.min(b, Math.min(r, g));
+
+        float H;
+
+        if (V == temp) {
+            H = 0;
+        } else {
+            final float vtemp = (float) (V - temp);
+            final float cr = (V - r) / vtemp;
+            final float cg = (V - g) / vtemp;
+            final float cb = (V - b) / vtemp;
+
+            if (r == V) {
+                H = cb - cg;
+            } else if (g == V) {
+                H = 2 + cr - cb;
+            } else {
+                H = 4 + cg - cr;
+            }
+
+            H /= 6.f;
+            if (H < 0) {
+                H++;
+            }
+        }
+
+        return H;
+    }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
new file mode 100644
index 0000000..3722320
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
@@ -0,0 +1,58 @@
+/*
+ * 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.systemui;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+
+/**
+ * An activity that exercises SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ */
+public class LightStatusBarActivity extends Activity {
+
+    private View mContent;
+
+    public void onCreate(Bundle bundle){
+        super.onCreate(bundle);
+
+        mContent = new View(this);
+        mContent.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+                LayoutParams.MATCH_PARENT));
+        setContentView(mContent);
+    }
+
+    public void setLightStatusBar(boolean lightStatusBar) {
+        int vis = getWindow().getDecorView().getSystemUiVisibility();
+        if (lightStatusBar) {
+            vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        } else {
+            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        }
+        getWindow().getDecorView().setSystemUiVisibility(vis);
+    }
+
+    public int getTop() {
+        return mContent.getLocationOnScreen()[1];
+    }
+
+    public int getWidth() {
+        return mContent.getWidth();
+    }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
new file mode 100644
index 0000000..b5bfd51
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
@@ -0,0 +1,235 @@
+/*
+ * 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.systemui;
+
+import android.app.ActivityManager;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Test for light status bar.
+ */
+public class LightStatusBarTests extends ActivityInstrumentationTestCase2<LightStatusBarActivity> {
+
+    public static final String TAG = "LightStatusBarTests";
+
+    public static final String DUMP_PATH = "/sdcard/lightstatustest.png";
+
+    public LightStatusBarTests() {
+        super(LightStatusBarActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // As the way to access Instrumentation is changed in the new runner, we need to inject it
+        // manually into ActivityInstrumentationTestCase2. ActivityInstrumentationTestCase2 will
+        // be marked as deprecated and replaced with ActivityTestRule.
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+    }
+
+    public void testLightStatusBarIcons() throws Throwable {
+        PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+                || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+            // No status bar on TVs and watches.
+            return;
+        }
+
+        if (!ActivityManager.isHighEndGfx()) {
+            // non-highEndGfx devices don't do colored system bars.
+            return;
+        }
+
+        requestLightStatusBar(Color.RED /* background */);
+        Thread.sleep(1000);
+
+        Bitmap bitmap = takeStatusBarScreenshot();
+        Stats s = evaluateLightStatusBarBitmap(bitmap, Color.RED /* background */);
+        boolean success = false;
+
+        try {
+            assertMoreThan("Not enough background pixels", 0.3f,
+                    (float) s.backgroundPixels / s.totalPixels(),
+                    "Is the status bar background showing correctly (solid red)?");
+
+            assertMoreThan("Not enough pixels colored as in the spec", 0.1f,
+                    (float) s.iconPixels / s.foregroundPixels(),
+                    "Are the status bar icons colored according to the spec "
+                            + "(60% black and 24% black)?");
+
+            assertLessThan("Too many lighter pixels lighter than the background", 0.05f,
+                    (float) s.sameHueLightPixels / s.foregroundPixels(),
+                    "Are the status bar icons dark?");
+
+            assertLessThan("Too many pixels with a changed hue", 0.05f,
+                    (float) s.unexpectedHuePixels / s.foregroundPixels(),
+                    "Are the status bar icons color-free?");
+
+            success = true;
+        } finally {
+            if (!success) {
+                Log.e(TAG, "Dumping failed bitmap to " + DUMP_PATH);
+                dumpBitmap(bitmap);
+            }
+        }
+    }
+
+    private void assertMoreThan(String what, float expected, float actual, String hint) {
+        if (!(actual > expected)) {
+            fail(what + ": expected more than " + expected * 100 + "%, but only got " + actual * 100
+                    + "%; " + hint);
+        }
+    }
+
+    private void assertLessThan(String what, float expected, float actual, String hint) {
+        if (!(actual < expected)) {
+            fail(what + ": expected less than " + expected * 100 + "%, but got " + actual * 100
+                    + "%; " + hint);
+        }
+    }
+
+    private void requestLightStatusBar(final int background) throws Throwable {
+        final LightStatusBarActivity activity = getActivity();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.getWindow().setStatusBarColor(background);
+                activity.setLightStatusBar(true);
+            }
+        });
+    }
+
+    private static class Stats {
+        int backgroundPixels;
+        int iconPixels;
+        int sameHueDarkPixels;
+        int sameHueLightPixels;
+        int unexpectedHuePixels;
+
+        int totalPixels() {
+            return backgroundPixels + iconPixels + sameHueDarkPixels
+                    + sameHueLightPixels + unexpectedHuePixels;
+        }
+
+        int foregroundPixels() {
+            return iconPixels + sameHueDarkPixels
+                    + sameHueLightPixels + unexpectedHuePixels;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{bg=%d, ic=%d, dark=%d, light=%d, bad=%d}",
+                    backgroundPixels, iconPixels, sameHueDarkPixels, sameHueLightPixels,
+                    unexpectedHuePixels);
+        }
+    }
+
+    private Stats evaluateLightStatusBarBitmap(Bitmap bitmap, int background) {
+        int iconColor = 0x99000000;
+        int iconPartialColor = 0x3d000000;
+
+        int mixedIconColor = mixSrcOver(background, iconColor);
+        int mixedIconPartialColor = mixSrcOver(background, iconPartialColor);
+
+        int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
+        bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+        Stats s = new Stats();
+        float eps = 0.005f;
+
+        for (int c : pixels) {
+            if (c == background) {
+                s.backgroundPixels++;
+                continue;
+            }
+
+            // What we expect the icons to be colored according to the spec.
+            if (c == mixedIconColor || c == mixedIconPartialColor) {
+                s.iconPixels++;
+                continue;
+            }
+
+            // Due to anti-aliasing, there will be deviations from the ideal icon color, but it
+            // should still be mostly the same hue.
+            float hueDiff = Math.abs(ColorUtils.hue(background) - ColorUtils.hue(c));
+            if (hueDiff < eps || hueDiff > 1 - eps) {
+                // .. it shouldn't be lighter than the original background though.
+                if (ColorUtils.brightness(c) > ColorUtils.brightness(background)) {
+                    s.sameHueLightPixels++;
+                } else {
+                    s.sameHueDarkPixels++;
+                }
+                continue;
+            }
+
+            s.unexpectedHuePixels++;
+        }
+
+        return s;
+    }
+
+    private void dumpBitmap(Bitmap bitmap) {
+        FileOutputStream fileStream = null;
+        try {
+            fileStream = new FileOutputStream(DUMP_PATH);
+            bitmap.compress(Bitmap.CompressFormat.PNG, 85, fileStream);
+            fileStream.flush();
+        } catch (Exception e) {
+            Log.e(TAG, "Dumping bitmap failed.", e);
+        } finally {
+            if (fileStream != null) {
+                try {
+                    fileStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private int mixSrcOver(int background, int foreground) {
+        int bgAlpha = Color.alpha(background);
+        int bgRed = Color.red(background);
+        int bgGreen = Color.green(background);
+        int bgBlue = Color.blue(background);
+
+        int fgAlpha = Color.alpha(foreground);
+        int fgRed = Color.red(foreground);
+        int fgGreen = Color.green(foreground);
+        int fgBlue = Color.blue(foreground);
+
+        return Color.argb(fgAlpha + (255 - fgAlpha) * bgAlpha / 255,
+                    fgRed + (255 - fgAlpha) * bgRed / 255,
+                    fgGreen + (255 - fgAlpha) * bgGreen / 255,
+                    fgBlue + (255 - fgAlpha) * bgBlue / 255);
+    }
+
+    private Bitmap takeStatusBarScreenshot() {
+        Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
+        return Bitmap.createBitmap(fullBitmap, 0, 0,
+                getActivity().getWidth(), getActivity().getTop());
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index e02de92..bba9a44 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -18,6 +18,8 @@
 
 import static android.telecom.cts.TestUtils.shouldTestTelecom;
 
+import android.content.Context;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.TelecomManager;
@@ -66,11 +68,15 @@
             return;
         }
 
+        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        int expectedRoute = am.isWiredHeadsetOn() ?
+                CallAudioState.ROUTE_WIRED_HEADSET : CallAudioState.ROUTE_EARPIECE;
+
         final Bundle extras = new Bundle();
         extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
         placeAndVerifyCall(extras);
         verifyConnectionForOutgoingCall();
-        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+        assertAudioRoute(mInCallCallbacks.getService(), expectedRoute);
     }
 
     public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
@@ -78,8 +84,12 @@
             return;
         }
 
+        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        int expectedRoute = am.isWiredHeadsetOn() ?
+                CallAudioState.ROUTE_WIRED_HEADSET : CallAudioState.ROUTE_EARPIECE;
+
         placeAndVerifyCall();
         verifyConnectionForOutgoingCall();
-        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+        assertAudioRoute(mInCallCallbacks.getService(), expectedRoute);
     }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
index ab4106e..e7130ba 100644
--- a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
@@ -110,16 +110,6 @@
         assertConnectionState(connection, Connection.STATE_DISCONNECTED);
     }
 
-    public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
-        if (!mShouldTestTelecom) {
-            return;
-        }
-
-        placeAndVerifyCall();
-        verifyConnectionForOutgoingCall();
-        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
-    }
-
     private void sendMediaButtonShortPress() throws Exception {
         sendMediaButtonPress(false /* longPress */);
     }
diff --git a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
index 176c50c..e15b45f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.SystemClock;
 import android.telephony.SmsManager;
@@ -84,6 +85,7 @@
     private Random mRandom;
     private SentReceiver mSentReceiver;
     private TelephonyManager mTelephonyManager;
+    private PackageManager mPackageManager;
 
     private static class SentReceiver extends BroadcastReceiver {
         private final Object mLock;
@@ -164,15 +166,26 @@
         mRandom = new Random();
         mTelephonyManager =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        mPackageManager = mContext.getPackageManager();
     }
 
     public void testSendMmsMessage() {
-        if (!mTelephonyManager.isSmsCapable()) {
-            Log.i(TAG, "testSendMmsMessage skipped: not SMS capable");
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            Log.i(TAG, "testSendMmsMessage skipped: no telephony available");
             return;
         }
 
         Log.i(TAG, "testSendMmsMessage");
+        // Prime the MmsService so that MMS config is loaded
+        final SmsManager smsManager = SmsManager.getDefault();
+        smsManager.getAutoPersisting();
+        // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+
         final Context context = getContext();
         // Register sent receiver
         mSentReceiver = new SentReceiver();
@@ -193,7 +206,7 @@
         // Send
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 context, 0, new Intent(ACTION_MMS_SENT), 0);
-        SmsManager.getDefault().sendMultimediaMessage(context,
+        smsManager.sendMultimediaMessage(context,
                 contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
         assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
         sendFile.delete();
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 41fe996..8b94d00 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -225,7 +225,10 @@
                 break;
 
             case TelephonyManager.PHONE_TYPE_NONE:
-                if (mCm.getNetworkInfo(ConnectivityManager.TYPE_WIFI) != null) {
+                boolean nwSupported = mCm.isNetworkSupported(mCm.TYPE_WIFI);
+                PackageManager packageManager = getContext().getPackageManager();
+                // only check serial number & MAC address if device report wifi feature
+                if (packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
                     assertSerialNumber();
                     assertMacAddress(getWifiMacAddress());
                 } else if (mCm.getNetworkInfo(ConnectivityManager.TYPE_BLUETOOTH) != null) {
diff --git a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
index 6eb09eb..9ab815f 100644
--- a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
@@ -94,20 +94,20 @@
         final long ONE_SECOND_IN_MS = 1000;
         assertEquals("0 minutes ago",
                 DateUtils.getRelativeTimeSpanString(mBaseTime - ONE_SECOND_IN_MS));
-        assertEquals("in 0 minutes",
+        assertEquals("In 0 minutes",
                 DateUtils.getRelativeTimeSpanString(mBaseTime + ONE_SECOND_IN_MS));
 
         final long ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS;
         assertEquals("1 minute ago", DateUtils.getRelativeTimeSpanString(0, ONE_MINUTE_IN_MS,
                 DateUtils.MINUTE_IN_MILLIS));
-        assertEquals("in 1 minute", DateUtils.getRelativeTimeSpanString(ONE_MINUTE_IN_MS, 0,
+        assertEquals("In 1 minute", DateUtils.getRelativeTimeSpanString(ONE_MINUTE_IN_MS, 0,
                 DateUtils.MINUTE_IN_MILLIS));
 
         final long ONE_HOUR_IN_MS = 60 * 60 * 1000;
         final long TWO_HOURS_IN_MS = 2 * ONE_HOUR_IN_MS;
         assertEquals("2 hours ago", DateUtils.getRelativeTimeSpanString(mBaseTime - TWO_HOURS_IN_MS,
                 mBaseTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_NUMERIC_DATE));
-        assertEquals("in 2 hours", DateUtils.getRelativeTimeSpanString(mBaseTime + TWO_HOURS_IN_MS,
+        assertEquals("In 2 hours", DateUtils.getRelativeTimeSpanString(mBaseTime + TWO_HOURS_IN_MS,
                 mBaseTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_NUMERIC_DATE));
     }
 
diff --git a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
index 10d08d0..482edb0 100644
--- a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
@@ -255,7 +255,7 @@
 
         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
                 KeyEvent.KEYCODE_DPAD_UP, noMetaEvent));
-        // |first line
+        // first lin|e
         // second line
         // last line
         assertSelection(0);
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 3c6028f..6514402 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -165,6 +165,9 @@
             // Wait for things to settle.
             getUiDevice().waitForIdle();
 
+            // Wait for Activity draw finish
+            getInstrumentation().waitForIdleSync();
+
             // Clear the window animation stats to be with a clean slate.
             uiAutomation.clearWindowAnimationFrameStats();
 
@@ -177,6 +180,9 @@
             // Wait for things to settle.
             getUiDevice().waitForIdle();
 
+            // Wait for Activity draw finish
+            getInstrumentation().waitForIdleSync();
+
             // Get the frame stats.
             WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
index cb62694..c97e020 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
@@ -28,13 +28,19 @@
  */
 public class SamplePointVerifier extends BitmapVerifier {
     private static final String TAG = "SamplePoint";
-    private Point[] mTestPoints;
-    private int[] mExpectedColors;
-    private int mTolerance = 20;
+    private static final int DEFAULT_TOLERANCE = 20;
+    private final Point[] mTestPoints;
+    private final int[] mExpectedColors;
+    private final int mTolerance;
 
     public SamplePointVerifier(Point[] testPoints, int[] expectedColors) {
+        this(testPoints, expectedColors, DEFAULT_TOLERANCE);
+    }
+
+    public SamplePointVerifier(Point[] testPoints, int[] expectedColors, int tolerance) {
         mTestPoints = testPoints;
         mExpectedColors = expectedColors;
+        mTolerance = tolerance;
     }
 
     @Override
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 2726dac..38777a2 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -16,6 +16,7 @@
 
 package android.uirendering.cts.testclasses;
 
+import android.content.pm.PackageManager;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -163,6 +164,9 @@
 
     @SmallTest
     public void testWebViewClipWithCircle() {
+        if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+            return; // no WebView to run test on
+        }
         createTest()
                 // golden client - draw a simple non-AA circle
                 .addCanvasClient(new CanvasClient() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
index 2061023..4582935 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
@@ -32,6 +32,7 @@
         if (getActivity().getOnTv()) {
             shadowColorValue = 0xBB;
         }
+        // Use a higher threshold (30) than default value (20);
         SamplePointVerifier verifier = new SamplePointVerifier(
                 new Point[] {
                         // view area
@@ -46,7 +47,8 @@
                         Color.WHITE,
                         Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
                         Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
-                });
+                },
+                30);
         createTest()
                 .addLayout(R.layout.simple_shadow_layout, null, true/* HW only */)
                 .runWithVerifier(verifier);
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 d585f5f..57c67bd 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -48,7 +48,7 @@
                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
         mHandler = new RenderSpecHandler();
         int uiMode = getResources().getConfiguration().uiMode;
-        mOnTv = (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) == Configuration.UI_MODE_TYPE_TELEVISION;
+        mOnTv = (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
     }
 
     public boolean getOnTv() {
diff --git a/tests/tests/voiceinteraction/AndroidTest.xml b/tests/tests/voiceinteraction/AndroidTest.xml
index 374c216..fa1ab70 100644
--- a/tests/tests/voiceinteraction/AndroidTest.xml
+++ b/tests/tests/voiceinteraction/AndroidTest.xml
@@ -19,7 +19,5 @@
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionApp.apk" />
     <option name="run-command:run-command"
          value="settings put secure voice_interaction_service android.voiceinteraction.service/.MainInteractionService" />
-    <option name="run-command:teardown-command"
-         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionTestCases.apk" />
 </configuration>
diff --git a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
index 1c9ee71..15069ec 100644
--- a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
@@ -23,7 +23,7 @@
 
       <activity android:name="TestApp"
                 android:label="Voice Interaction Test App"
-                android:theme="@android:style/Theme.Material.Light">
+                android:theme="@android:style/Theme.DeviceDefault">
           <intent-filter>
               <action android:name="android.intent.action.TEST_APP" />
               <category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
index 0a3974d..e3be691 100644
--- a/tests/tests/voicesettings/AndroidTest.xml
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -17,7 +17,5 @@
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsService.apk" />
     <option name="run-command:run-command"
          value="settings put secure voice_interaction_service android.voicesettings.service/.MainInteractionService" />
-    <option name="run-command:teardown-command"
-         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsTestCases.apk" />
 </configuration>
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
index c2b7e18..aff1160 100644
--- a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
@@ -24,6 +24,7 @@
 import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
 import static android.provider.Settings.EXTRA_BATTERY_SAVER_MODE_ENABLED;
 
+import android.app.VoiceInteractor;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncTask;
@@ -138,9 +139,10 @@
 
     @Override
     public void onRequestCompleteVoice(CompleteVoiceRequest request) {
-        CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
+        VoiceInteractor.Prompt prompt = request.getVoicePrompt();
+        CharSequence message = (prompt != null ? prompt.getVoicePromptAt(0) : "(none)");
         Log.i(TAG, "in Session testcasetype = " + mTestType +
-                ", onRequestCompleteVoice recvd. message = " + prompt);
+                ", onRequestCompleteVoice recvd. message = " + message);
         AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setCompleteReq(true);
         newTask().execute(asyncTaskArg);
     }
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
index 4597651..9ce743e 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -16,6 +16,8 @@
 
 package android.voicesettings.cts;
 
+import static android.provider.Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE;
+
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.util.Log;
@@ -33,6 +35,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!isIntentSupported(ACTION_VOICE_CONTROL_AIRPLANE_MODE)) {
+            Log.e(TAG, "Voice intent for Airplane Mode NOT supported. existing the test");
+            return;
+        }
         int mode;
         try {
             mode = getMode();
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
index 3d1357a..983e27b 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -16,6 +16,8 @@
 
 package android.voicesettings.cts;
 
+import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
+
 import android.content.Context;
 import android.os.PowerManager;
 import android.util.Log;
@@ -30,6 +32,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!isIntentSupported(ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE)) {
+            Log.e(TAG, "Voice intent for Battery Saver Mode NOT supported. existing the test");
+            return;
+        }
         startTestActivity("BATTERYSAVER_MODE");
         boolean modeIsOn = isModeOn();
         Log.i(TAG, "Before testing, BATTERYSAVER_MODE is set to: " + modeIsOn);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
index 5386497..98b9c29 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
@@ -53,10 +54,23 @@
 
     @Override
     protected void tearDown() throws Exception {
-        mContext.unregisterReceiver(mActivityDoneReceiver);
+        if (mActivityDoneReceiver != null) {
+            mContext.unregisterReceiver(mActivityDoneReceiver);
+        }
         super.tearDown();
     }
 
+    protected boolean isIntentSupported(String intentStr) {
+        Intent intent = new Intent(intentStr);
+        final PackageManager manager = mContext.getPackageManager();
+        assertNotNull(manager);
+        if (manager.resolveActivity(intent, 0) == null) {
+            Log.i(TAG, "No Voice Activity found for the intent: " + intentStr);
+            return false;
+        }
+        return true;
+    }
+
     protected void startTestActivity(String intentSuffix) {
         Intent intent = new Intent();
         intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index ca86918..420da8f 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -16,6 +16,7 @@
 
 package android.voicesettings.cts;
 
+import static android.provider.Settings.ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE;
 import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
 import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
 
@@ -39,6 +40,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!isIntentSupported(ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE)) {
+            Log.e(TAG, "Voice intent for Zen Mode NOT supported. existing the test");
+            return;
+        }
         int mode;
         try {
             mode = getMode();
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 fa930df..782e6ab 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 = "6.0_r0";
+    public static final String CTS_BUILD_VERSION = "6.0_r1";
     public static final String CTS_PACKAGE = "com.android.cts.tradefed.testtype";
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index f5a3d02..45224f6 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -254,12 +254,12 @@
             if (perfResult == null) {
                 perfResult = CtsHostStore.removeCtsResult(mDeviceSerial, mAbi, test.toString());
             }
-            if (perfResult != null) {
+            Test result = findTest(test);
+            if (perfResult != null && !result.getResult().equals(CtsTestStatus.FAIL)) {
                 // CTS result is passed in Summary++++Details format.
                 // Extract Summary and Details, and pass them.
                 Matcher m = mCtsLogPattern.matcher(perfResult);
                 if (m.find()) {
-                    Test result = findTest(test);
                     result.setResultStatus(CtsTestStatus.PASS);
                     result.setSummary(m.group(1));
                     result.setDetails(m.group(2));
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
index 43aaf98..6f4d42d 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
@@ -63,7 +63,7 @@
     private static final BatchRunConfiguration DEFAULT_CONFIG =
         new BatchRunConfiguration("rgba8888d24s8", "unspecified", "window");
 
-    private static final int UNRESPOSIVE_CMD_TIMEOUT_MS = 60000; // one minute
+    private static final int UNRESPOSIVE_CMD_TIMEOUT_MS = 10*60*1000; // ten minutes
 
     private final String mPackageName;
     private final String mName;
@@ -427,6 +427,7 @@
                 if (!mGotTestResult) {
                     result.allInstancesPassed = false;
                     result.errorMessages.put(mRunConfig, INCOMPLETE_LOG_MESSAGE);
+                    CLog.i("Test %s failed as it ended before receiving result.", mCurrentTestId);
                 }
 
                 if (mLogData && mCurrentTestLog != null && mCurrentTestLog.length() > 0) {
@@ -477,6 +478,7 @@
                 mPendingResults.get(mCurrentTestId)
                         .errorMessages.put(mRunConfig, codeError + ": " + details);
                 mGotTestResult = true;
+                CLog.e("Got invalid result code '%s' for test %s", code, mCurrentTestId);
             }
         }
 
@@ -950,6 +952,7 @@
         private void killDeqpProcess() throws DeviceNotAvailableException,
                 ProcessKillFailureException {
             for (Integer processId : getDeqpProcessPids()) {
+                CLog.i("Killing deqp device process with ID %d", processId);
                 mDevice.executeShellCommand(String.format("kill -9 %d", processId));
             }
 
@@ -958,6 +961,7 @@
             // check that processes actually died
             if (getDeqpProcessPids().iterator().hasNext()) {
                 // a process is still alive, killing failed
+                CLog.w("Failed to kill all deqp processes on device");
                 throw new ProcessKillFailureException();
             }
         }
@@ -1336,15 +1340,19 @@
                     UNRESPOSIVE_CMD_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         } catch (TimeoutException ex) {
             // Opening connection timed out
+            CLog.e("Opening connection timed out for command: '%s'", command);
             throw new AdbComLinkOpenError("opening connection timed out", ex);
         } catch (AdbCommandRejectedException ex) {
             // Command rejected
+            CLog.e("Device rejected command: '%s'", command);
             throw new AdbComLinkOpenError("command rejected", ex);
         } catch (IOException ex) {
             // shell command channel killed
+            CLog.e("Channel died for command: '%s'", command);
             throw new AdbComLinkKilledError("command link killed", ex);
         } catch (ShellCommandUnresponsiveException ex) {
             // shell command halted
+            CLog.e("No output from command in %d ms: '%s'", UNRESPOSIVE_CMD_TIMEOUT_MS, command);
             throw new AdbComLinkKilledError("command link hung", ex);
         }
     }
@@ -1460,11 +1468,14 @@
         // interrupted, try to recover
         if (interruptingError != null) {
             if (interruptingError instanceof AdbComLinkOpenError) {
+                CLog.i("Recovering from comm link error");
                 mDeviceRecovery.recoverConnectionRefused();
             } else if (interruptingError instanceof AdbComLinkKilledError) {
+                CLog.i("Recovering from comm link killed");
                 mDeviceRecovery.recoverComLinkKilled();
             } else if (interruptingError instanceof RunInterruptedException) {
                 // external run interruption request. Terminate immediately.
+                CLog.i("Run termination requested. Throwing forward.");
                 throw (RunInterruptedException)interruptingError;
             } else {
                 CLog.e(interruptingError);
@@ -1473,6 +1484,7 @@
 
             // recoverXXX did not throw => recovery succeeded
         } else if (!parser.wasSuccessful()) {
+            CLog.i("Parse not successful. Will attempt comm link recovery.");
             mDeviceRecovery.recoverComLinkKilled();
             // recoverXXX did not throw => recovery succeeded
         }
@@ -1495,8 +1507,10 @@
                 // This is required so that a consistently crashing or non-existent tests will
                 // not cause futile (non-terminating) re-execution attempts.
                 if (mInstanceListerner.getCurrentTestId() != null) {
+                    CLog.w("Test '%s' started, but not completed", onlyTest);
                     mInstanceListerner.abortTest(onlyTest, INCOMPLETE_LOG_MESSAGE);
                 } else {
+                    CLog.w("Test '%s' could not start", onlyTest);
                     mInstanceListerner.abortTest(onlyTest, NOT_EXECUTABLE_LOG_MESSAGE);
                 }
             } else if (wasTestExecuted) {
@@ -1827,6 +1841,7 @@
                 uninstallTestApk();
             } else {
                 // Pass all tests if OpenGL ES version is not supported
+                CLog.i("Package %s not supported by the device. Tests trivially pass.", mPackageName);
                 fakePassTests(listener);
             }
         } catch (CapabilityQueryFailureException ex) {
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index a048ddc..2eb5145 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -471,9 +471,13 @@
           'android.alarmclock.cts.SnoozeAlarmTest#testAll',
       ],
       'android.assist' : [
-          'android.assist.cts.ExtraAssistDataTest',
           'android.assist.cts.AssistantContentViewTest',
+          'android.assist.cts.ExtraAssistDataTest',
+          'android.assist.cts.FocusChangeTest',
+          'android.assist.cts.LargeViewHierarchyTest',
           'android.assist.cts.ScreenshotTest',
+          'android.assist.cts.TextViewTest',
+          'android.assist.cts.WebViewTest',
       ],
       'android.calllog' : [
           'android.calllog.cts.CallLogBackupTest#testSingleCallBackup',
@@ -492,6 +496,9 @@
       'android.voicesettings' : [
           'android.voicesettings.cts.ZenModeTest#testAll',
       ],
+      'com.android.cts.systemui' : [
+          'com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons',
+      ],
       'com.android.cts.app.os' : [
           'com.android.cts.app.os.OsHostTests#testNonExportedActivities',
       ],