Squashed mnc-dev changes:

This contains all of the changes from b54aa51 to
791e51a on mnc-dev, except the changes
to tests/tests/security.

Bug: 24846656
Change-Id: I01f53a1a238ac49f86928e0e22796dc73e0e34af
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 35cfc07..1072684 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -35,12 +35,13 @@
     """
     NAME = os.path.basename(__file__).split(".")[0]
 
-    RELATIVE_ERROR_TOLERANCE = 0.1
-    # List of variances for R,G,B.
-    variances = [[],[],[]]
+    NUM_SAMPLES_PER_MODE = 4
+    SNR_TOLERANCE = 3 # unit in db
+    # List of SNRs for R,G,B.
+    snrs = [[], [], []]
 
-    # Reference (baseline) variance for each of R,G,B.
-    ref_variance = []
+    # Reference (baseline) SNR for each of R,G,B.
+    ref_snr = []
 
     nr_modes_reported = []
 
@@ -60,8 +61,8 @@
                 rgb_image,
                 "%s_low_gain.jpg" % (NAME))
         rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
-        ref_variance = its.image.compute_image_variances(rgb_tile)
-        print "Ref variances:", ref_variance
+        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
@@ -70,58 +71,74 @@
             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;
 
-            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)
-            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_vars = its.image.compute_image_variances(rgb_tile)
+            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):
-                variance = rgb_vars[chan]
-                variances[chan].append(variance / ref_variance[chan])
-        print "Variances with NR mode [0,1,2]:", variances
+                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 5c188b7..5e8ab90 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -363,6 +363,7 @@
             <meta-data android:name="test_category" android:value="@string/test_category_security" />
             <meta-data android:name="test_excluded_features"
                        android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.fingerprint" />
         </activity>
         <activity android:name=".security.ScreenLockBoundKeysTest"
                 android:label="@string/sec_lock_bound_key_test"
@@ -753,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
@@ -762,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"
@@ -1397,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">
@@ -1424,6 +1430,8 @@
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_KEYGUARD_DISABLED_FEATURES" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_LOCKNOW" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG" />
                 <category android:name="android.intent.category.DEFAULT"></category>
             </intent-filter>
         </activity>
@@ -1451,11 +1459,87 @@
 
         <activity android:name=".managedprovisioning.CrossProfileTestActivity">
             <intent-filter>
-                <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE" />
-                <!-- 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="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_PERSONAL" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_WORK" />
+                <!-- 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" />
-                <category android:name="android.intent.category.DEFAULT"></category>
+                <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" />
+                <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>
 
@@ -1573,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>
@@ -1612,30 +1708,46 @@
             <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_out_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" />
-            -->
+            <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"
                   android:label="@string/audio_loopback_test">
             <intent-filter>
@@ -1644,6 +1756,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..ceedf1c 100644
--- a/apps/CtsVerifier/res/layout/audio_dev_notify.xml
+++ b/apps/CtsVerifier/res/layout/audio_dev_notify.xml
@@ -13,19 +13,51 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        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:padding="10dip"
+        android:padding="20dp"
         android:orientation="vertical">
 
-  <TextView
+        <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: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_devices_notification_instructions" />
+      android:id="@+id/info_text"/>
 
   <LinearLayout
       android:layout_width="match_parent"
@@ -51,4 +83,5 @@
 
   <include layout="@layout/pass_fail_buttons" />
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
+</ScrollView>
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 61%
copy from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
copy to apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
index cef30d6..60a12ef 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
@@ -14,52 +14,51 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        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:padding="10dip"
+        android:padding = "20dp"
         android:orientation="vertical">
 
-  <TextView
+        <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: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_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"
@@ -96,4 +95,5 @@
 
   <include layout="@layout/pass_fail_buttons" />
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
+</ScrollView>
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 61%
rename from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
rename to apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
index cef30d6..d039691 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
@@ -14,19 +14,52 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        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:padding="10dip"
+        android:padding="20dp"
         android:orientation="vertical">
 
-  <TextView
+        <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: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_dev_routingnotification_instructions" />
+      android:text="@string/audio_output_routingnotification_instructions" />
 
   <LinearLayout
       android:layout_width="match_parent"
@@ -61,39 +94,7 @@
       </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
+</LinearLayout>
+</ScrollView>
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 77566a3..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">
@@ -1436,12 +1454,13 @@
     <string name="provisioning_byod_profile_visible">Profile-aware accounts settings</string>
     <string name="provisioning_byod_admin_visible">Profile-aware device administrator settings</string>
     <string name="provisioning_byod_workapps_visible">Badged work apps visible in Launcher</string>
-    <string name="provisioning_byod_cross_profile">Open app cross profiles</string>
-    <string name="provisioning_byod_cross_profile_app_personal">
-        You selected the CTS Verifier option.
-    </string>
+    <string name="provisioning_byod_cross_profile_from_personal">Open app cross profiles from the personal side</string>
+    <string name="provisioning_byod_cross_profile_from_work">Open app cross profiles from the work side</string>
+    <string name="provisioning_app_linking">App links from the work side</string>
+    <string name="provisioning_byod_cross_profile_app_personal">You selected the personal option.</string>
     <string name="provisioning_byod_cross_profile_app_work">You selected the Work option.</string>
-    <string name="provisioning_byod_cross_profile_instruction">
+    <string name="provisioning_byod_cross_profile_app_ctsverifier"> You selected the ctsverifier option </string>
+    <string name="provisioning_byod_cross_profile_from_personal_instruction">
         Please press the Go button to start an action.\n
         \n
         You should be asked to choose either \"CTS Verifier\" or \"Work\" to complete the action.
@@ -1449,6 +1468,25 @@
         \n
         Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
     </string>
+    <string name="provisioning_byod_cross_profile_from_work_instruction">
+        Please press the Go button to start an action.\n
+        \n
+        You should be asked to choose either \"CTS Verifier\" or \"Personal\" to complete the action.
+        Pressing either should bring up a page stating your choice.\n
+        \n
+        Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
+    </string>
+    <string name="provisioning_byod_app_linking_instruction">
+        Please press the Go button to start an action.\n
+        \n
+        You should be asked to choose either \"CTS Verifier\" or \"Personal\" to complete the action.\n
+        - If you choose \"CTS Verifier\", you should see a page stating your chose \"CTS Verifier\".\n
+        - If you choose \"Personal\", you should be presented with another dialog between \"CTS Verifier\"
+        and some other apps. In this case, you should choose \"CTS verifier\".\n
+        You should then see a page stating you chose \"Personal\".\n
+        \n
+        Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
+    </string>
     <string name="provisioning_byod_keyguard_disabled_features">Keyguard disabled features</string>
     <string name="provisioning_byod_keyguard_disabled_features_info">
         This test exercises Keyguard Disabled Features. Follow instructions above.
@@ -1640,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>
@@ -1875,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">
@@ -1964,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>
 
@@ -1984,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>
@@ -2013,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">
@@ -2055,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 82%
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..e253635 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.cts.verifier.audio;
 
-import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
 import android.content.Context;
@@ -34,10 +33,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 HeadsetHonorSystemActivity {
     Context mContext;
 
     TextView mConnectView;
@@ -62,6 +61,11 @@
     }
 
     @Override
+    protected void enableTestButtons(boolean enabled) {
+        // Nothing to do.
+    }
+
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.audio_dev_notify);
@@ -71,6 +75,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) {
@@ -82,6 +89,9 @@
         AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
         audioManager.registerAudioDeviceCallback(new TestAudioDeviceCallback(), null);
 
+        // "Honor System" buttons
+        super.setup();
+
         setPassFailButtonClickListeners();
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
new file mode 100644
index 0000000..eefa9e4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
@@ -0,0 +1,120 @@
+/*
+ * 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.audio;
+
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Tests AudioRecord (re)Routing messages.
+ */
+public class AudioInputRoutingNotificationsActivity extends HeadsetHonorSystemActivity {
+    private static final String TAG = "AudioInputRoutingNotificationsActivity";
+
+    Button recordBtn;
+    Button stopBtn;
+
+    Context mContext;
+
+    int mNumRecordNotifications = 0;
+
+    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_recordBtn:
+                    mAudioRecorder.start();
+                    break;
+
+                case R.id.audio_routingnotification_recordStopBtn:
+                    mAudioRecorder.stop();
+                    break;
+            }
+        }
+    }
+
+    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);
+        }
+    }
+
+    protected void enableTestButtons(boolean enabled) {
+        recordBtn.setEnabled(enabled);
+        stopBtn.setEnabled(enabled);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.audio_input_routingnotifications_test);
+
+        Button btn;
+        recordBtn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
+        recordBtn.setOnClickListener(mBtnClickListener);
+        stopBtn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
+        stopBtn.setOnClickListener(mBtnClickListener);
+
+        mContext = this;
+
+        AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
+        audioRecord.addOnRoutingChangedListener(
+            new AudioRecordRoutingChangeListener(), new Handler());
+
+        // "Honor System" buttons
+        super.setup();
+
+        setPassFailButtonClickListeners();
+    }
+
+    @Override
+    public void onBackPressed () {
+        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 82%
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..ad8ba68 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.cts.verifier.audio;
 
-import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
 import android.content.Context;
@@ -34,10 +33,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 HeadsetHonorSystemActivity {
     Context mContext;
 
     TextView mConnectView;
@@ -62,6 +61,11 @@
     }
 
     @Override
+    protected void enableTestButtons(boolean enabled) {
+        // Nothing to do.
+    }
+
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.audio_dev_notify);
@@ -71,6 +75,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) {
@@ -82,6 +89,9 @@
         AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
         audioManager.registerAudioDeviceCallback(new TestAudioDeviceCallback(), null);
 
+        // "Honor System" buttons
+        super.setup();
+
         setPassFailButtonClickListeners();
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
new file mode 100644
index 0000000..a6d8846
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
@@ -0,0 +1,120 @@
+/*
+ * 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.audio;
+
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Tests AudioTrack and AudioRecord (re)Routing messages.
+ */
+public class AudioOutputRoutingNotificationsActivity extends HeadsetHonorSystemActivity {
+    private static final String TAG = "AudioOutputRoutingNotificationsActivity";
+
+    Context mContext;
+
+    Button playBtn;
+    Button stopBtn;
+
+    private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+    int mNumTrackNotifications = 0;
+
+    TrivialPlayer mAudioPlayer = new TrivialPlayer();
+
+    private class OnBtnClickListener implements OnClickListener {
+        @Override
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.audio_routingnotification_playBtn:
+                    mAudioPlayer.start();
+                    break;
+
+                case R.id.audio_routingnotification_playStopBtn:
+                    mAudioPlayer.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);
+        }
+    }
+
+    @Override
+    protected void enableTestButtons(boolean enabled) {
+        playBtn.setEnabled(enabled);
+        stopBtn.setEnabled(enabled);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.audio_output_routingnotifications_test);
+
+        mContext = this;
+
+        playBtn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
+        playBtn.setOnClickListener(mBtnClickListener);
+        stopBtn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
+        stopBtn.setOnClickListener(mBtnClickListener);
+
+        AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
+        audioTrack.addOnRoutingChangedListener(
+            new AudioTrackRoutingChangeListener(), new Handler());
+
+        // "Honor System" buttons
+        super.setup();
+
+        setPassFailButtonClickListeners();
+    }
+
+    @Override
+    public void onBackPressed () {
+        mAudioPlayer.shutDown();
+        super.onBackPressed();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
deleted file mode 100644
index b6a4255..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.audio;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.Context;
-
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
-import android.media.AudioRecord;
-import android.media.AudioTrack;
-
-import android.os.Bundle;
-import android.os.Handler;
-
-import android.util.Log;
-
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import android.widget.Button;
-import android.widget.TextView;
-
-/**
- * Tests AudioTrack and AudioRecord (re)Routing messages.
- */
-public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
-    private static final String TAG = "AudioRoutingNotificationsActivity";
-
-    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;
-            }
-        }
-    }
-
-    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++;
-            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);
-
-        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;
-
-        AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
-        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/audio/HeadsetHonorSystemActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HeadsetHonorSystemActivity.java
new file mode 100644
index 0000000..a82b994
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HeadsetHonorSystemActivity.java
@@ -0,0 +1,83 @@
+/*
+ * 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.audio;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
+import android.content.Context;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+//import android.widget.TextView;
+
+abstract class HeadsetHonorSystemActivity extends PassFailButtons.Activity {
+    private static final String TAG = "HeadsetHonorSystemActivity";
+
+    private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+    abstract protected void enableTestButtons(boolean enabled);
+
+    private void recordHeadsetPortFound(boolean found) {
+        getReportLog().addValue(
+                "User Reported Headset Port",
+                found ? 1.0 : 0,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+    }
+
+    protected void setup() {
+        // The "Honor" system buttons
+        ((Button)findViewById(R.id.audio_general_headset_no)).
+            setOnClickListener(mBtnClickListener);
+        ((Button)findViewById(R.id.audio_general_headset_yes)).
+            setOnClickListener(mBtnClickListener);
+
+        enableTestButtons(false);
+    }
+
+    private class OnBtnClickListener implements OnClickListener {
+        @Override
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.audio_general_headset_no:
+                    Log.i(TAG, "User denies Headset Port existence");
+                    enableTestButtons(false);
+                    recordHeadsetPortFound(false);
+                    break;
+
+                case R.id.audio_general_headset_yes:
+                    Log.i(TAG, "User confirms Headset Port existence");
+                    enableTestButtons(true);
+                    recordHeadsetPortFound(true);
+                    break;
+            }
+        }
+    }
+
+}
\ No newline at end of file
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/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 51e0a62..3c108da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -232,6 +232,7 @@
 
     public static final Feature[] ALL_MNC_FEATURES = {
             new Feature(PackageManager.FEATURE_MIDI, false),
+            new Feature(PackageManager.FEATURE_AUDIO_PRO, false),
     };
 
     @Override
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 0200a4f..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;
@@ -59,7 +60,9 @@
     private DialogTestListItem mProfileAccountVisibleTest;
     private DialogTestListItem mDeviceAdminVisibleTest;
     private DialogTestListItem mWorkAppVisibleTest;
-    private DialogTestListItem mCrossProfileIntentFiltersTest;
+    private DialogTestListItem mCrossProfileIntentFiltersTestFromPersonal;
+    private DialogTestListItem mCrossProfileIntentFiltersTestFromWork;
+    private DialogTestListItem mAppLinkingTest;
     private DialogTestListItem mDisableNonMarketTest;
     private DialogTestListItem mEnableNonMarketTest;
     private DialogTestListItem mWorkNotificationBadgedTest;
@@ -78,6 +81,7 @@
     private DialogTestListItem mCrossProfileAudioCaptureSupportTest;
     private TestListItem mKeyguardDisabledFeaturesTest;
     private DialogTestListItem mDisableNfcBeamTest;
+    private TestListItem mAuthenticationBoundKeyTest;
 
     public ByodFlowTestActivity() {
         super(R.layout.provisioning_byod,
@@ -264,20 +268,39 @@
                 R.string.provisioning_byod_print_settings_instruction,
                 new Intent(Settings.ACTION_PRINT_SETTINGS));
 
-        Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+        Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
+        intent.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, false);
         Intent chooser = Intent.createChooser(intent,
                 getResources().getString(R.string.provisioning_cross_profile_chooser));
-        mCrossProfileIntentFiltersTest = new DialogTestListItem(this,
-                R.string.provisioning_byod_cross_profile,
-                "BYOD_CrossProfileIntentFiltersTest",
-                R.string.provisioning_byod_cross_profile_instruction,
+        mCrossProfileIntentFiltersTestFromPersonal = new DialogTestListItem(this,
+                R.string.provisioning_byod_cross_profile_from_personal,
+                "BYOD_CrossProfileIntentFiltersTestFromPersonal",
+                R.string.provisioning_byod_cross_profile_from_personal_instruction,
                 chooser);
 
+        mCrossProfileIntentFiltersTestFromWork = new DialogTestListItem(this,
+                R.string.provisioning_byod_cross_profile_from_work,
+                "BYOD_CrossProfileIntentFiltersTestFromWork",
+                R.string.provisioning_byod_cross_profile_from_work_instruction,
+                new Intent(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG));
+
+        mAppLinkingTest = new DialogTestListItem(this,
+                R.string.provisioning_app_linking,
+                "BYOD_AppLinking",
+                R.string.provisioning_byod_app_linking_instruction,
+                new Intent(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG));
+
         mKeyguardDisabledFeaturesTest = TestListItem.newTest(this,
                 R.string.provisioning_byod_keyguard_disabled_features,
                 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,
@@ -287,7 +310,7 @@
                 checkIntentFilters();
             }
         };
-        
+
         Intent permissionCheckIntent = new Intent(
                 PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
         mPermissionLockdownTest = new DialogTestListItem(this,
@@ -314,12 +337,15 @@
         adapter.add(mDataUsageSettingsVisibleTest);
         adapter.add(mPrintSettingsVisibleTest);
 
-        adapter.add(mCrossProfileIntentFiltersTest);
+        adapter.add(mCrossProfileIntentFiltersTestFromPersonal);
+        adapter.add(mCrossProfileIntentFiltersTestFromWork);
+        adapter.add(mAppLinkingTest);
         adapter.add(mDisableNonMarketTest);
         adapter.add(mEnableNonMarketTest);
         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.
@@ -495,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),
@@ -503,4 +530,5 @@
                     PackageManager.DONT_KILL_APP);
         }
     }
+
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 3d7d42d..9ea5061 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.UserManager;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.support.v4.content.FileProvider;
@@ -84,6 +85,18 @@
     public static final String ACTION_CHECK_INTENT_FILTERS =
             "com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS";
 
+    // Primary -> managed intent: will send a cross profile intent and check if the user sees an
+    // intent picker dialog and can open the apps.
+    public static final String ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG =
+            "com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG";
+
+    // Primary -> managed intent: will send an app link intent and check if the user sees a
+    // dialog and can open the apps. This test is extremely similar to
+    // ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG, but the intent used is a web intent, and there is
+    // some behavior which is specific to web intents.
+    public static final String ACTION_TEST_APP_LINKING_DIALOG =
+            "com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG";
+
     public static final int RESULT_FAILED = RESULT_FIRST_USER;
 
     private static final int REQUEST_INSTALL_PACKAGE = 1;
@@ -211,6 +224,16 @@
             startActivity(testNfcBeamIntent);
             finish();
             return;
+        } else if (action.equals(ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG)) {
+            sendIntentInsideChooser(new Intent(
+                    CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL));
+        } else if (action.equals(ACTION_TEST_APP_LINKING_DIALOG)) {
+            mDevicePolicyManager.addUserRestriction(
+                    DeviceAdminTestReceiver.getReceiverComponentName(),
+                    UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
+            Intent toSend = new Intent(Intent.ACTION_VIEW);
+            toSend.setData(Uri.parse("http://com.android.cts.verifier"));
+            sendIntentInsideChooser(toSend);
         }
         // This activity has no UI and is only used to respond to CtsVerifier in the primary side.
         finish();
@@ -346,6 +369,13 @@
                 DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
     }
 
+    private void sendIntentInsideChooser(Intent toSend) {
+        toSend.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, true);
+        Intent chooser = Intent.createChooser(toSend,
+                getResources().getString(R.string.provisioning_cross_profile_chooser));
+        startActivity(chooser);
+    }
+
     @Override
     public void onDialogClose() {
         finish();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
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/CrossProfileTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
index 6c38e12..3f316f21 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
@@ -35,7 +35,12 @@
  */
 public class CrossProfileTestActivity extends Activity {
     // Intent for app in both profiles
-    public static final String ACTION_CROSS_PROFILE = "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE";
+    public static final String ACTION_CROSS_PROFILE_TO_PERSONAL =
+            "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_PERSONAL";
+    public static final String ACTION_CROSS_PROFILE_TO_WORK =
+            "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_WORK";
+    public static final String EXTRA_STARTED_FROM_WORK
+            = "com.android.cts.verifier.managedprovisioning.STARTED_FROM_WORK";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -45,9 +50,15 @@
 
         // Check if we are running in the work or personal side, by testing if currently we are the
         // profile owner or not.
-        textView.setText(isProfileOwner() ? R.string.provisioning_byod_cross_profile_app_work
-                : R.string.provisioning_byod_cross_profile_app_personal);
-
+        boolean inWorkProfile = isProfileOwner();
+        boolean startedFromWork = getIntent().getBooleanExtra(EXTRA_STARTED_FROM_WORK, false);
+        if (inWorkProfile && !startedFromWork) {
+            textView.setText(R.string.provisioning_byod_cross_profile_app_work);
+        } else if (!inWorkProfile && startedFromWork) {
+            textView.setText(R.string.provisioning_byod_cross_profile_app_personal);
+        } else { // started from the same side we're currently running in
+            textView.setText(R.string.provisioning_byod_cross_profile_app_ctsverifier);
+        }
         findViewById(R.id.button_finish).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
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 008091b..ed6b5eb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -64,7 +64,9 @@
             filter.addAction(ByodHelperActivity.ACTION_KEYGUARD_DISABLED_FEATURES);
             filter.addAction(ByodHelperActivity.ACTION_LOCKNOW);
             filter.addAction(ByodHelperActivity.ACTION_TEST_NFC_BEAM);
-            filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+            filter.addAction(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG);
+            filter.addAction(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG);
+            filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
             filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
             filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN);
             filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
@@ -72,12 +74,14 @@
             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);
 
             // Work -> primary direction
             filter = new IntentFilter();
             filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
+            filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL);
             dpm.addCrossProfileIntentFilter(getWho(context), filter,
                     DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
 
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/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
index 8e72ebb..0728fb5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -31,6 +31,8 @@
 
     private static final String TAG = "ScreenPinningTestActivity";
     private static final String KEY_CURRENT_TEST = "keyCurrentTest";
+    private static final long TASK_MODE_CHECK_DELAY = 200;
+    private static final int MAX_TASK_MODE_CHECK_COUNT = 5;
 
     private Test[] mTests;
     private int mTestIndex;
@@ -203,10 +205,18 @@
                 return;
             }
             stopLockTask();
-            if (!mActivityManager.isInLockTaskMode()) {
-                succeed();
-            } else {
-                error(R.string.error_screen_pinning_couldnt_exit);
+            for (int retry = MAX_TASK_MODE_CHECK_COUNT; retry > 0; retry--) {
+                try {
+                    Thread.sleep(TASK_MODE_CHECK_DELAY);
+                } catch (InterruptedException e) {
+                }
+                Log.d(TAG, "Check unpin ... " + retry);
+                if (!mActivityManager.isInLockTaskMode()) {
+                    succeed();
+                    break;
+                } else if (retry == 1) {
+                    error(R.string.error_screen_pinning_couldnt_exit);
+                }
             }
         };
     };
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/RVCVCameraPreview.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
index a5b58f6..fa89b71 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
@@ -23,9 +23,9 @@
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
+import android.view.ViewGroup;
 
 import java.io.IOException;
-import java.util.List;
 
 /** Camera preview class */
 public class RVCVCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
@@ -34,15 +34,16 @@
 
     private SurfaceHolder mHolder;
     private Camera mCamera;
+    private float mAspect;
+    private int mRotation;
 
     /**
      * Constructor
      * @param context Activity context
-     * @param camera Camera object to be previewed
      */
-    public RVCVCameraPreview(Context context, Camera camera) {
+    public RVCVCameraPreview(Context context) {
         super(context);
-        mCamera = camera;
+        mCamera = null;
         initSurface();
     }
 
@@ -55,8 +56,10 @@
         super(context, attrs);
     }
 
-    public void init(Camera camera) {
+    public void init(Camera camera, float aspectRatio, int rotation)  {
         this.mCamera = camera;
+        mAspect = aspectRatio;
+        mRotation = rotation;
         initSurface();
     }
 
@@ -86,6 +89,20 @@
         try {
             mCamera.setPreviewDisplay(holder);
             mCamera.startPreview();
+            int v_height = getHeight();
+            int v_width = getWidth();
+            ViewGroup.LayoutParams layout = getLayoutParams();
+            if ( (float)v_height/v_width  >
+                    mAspect) {
+                layout.height = (int)(v_width * mAspect);
+                layout.width = v_width;
+            }else {
+                layout.width = (int)(v_height / mAspect);
+                layout.height = v_height;
+            }
+            Log.d(TAG, String.format("Layout (%d, %d) -> (%d, %d)", v_width, v_height,
+                    layout.width, layout.height));
+            setLayoutParams(layout);
         } catch (IOException e) {
             if (LOCAL_LOGD) Log.d(TAG, "Error when starting camera preview: " + e.getMessage());
         }
@@ -111,8 +128,7 @@
         // stop preview before making changes
         mCamera.stopPreview();
 
-        // the activity using this view is locked to this orientation, so hard code is fine
-        mCamera.setDisplayOrientation(90);
+        mCamera.setDisplayOrientation(mRotation);
 
         //do the same as if it is created again
         surfaceCreated(holder);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
index f90b27c..be5ec52 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
@@ -46,6 +46,7 @@
 import java.io.OutputStreamWriter;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.List;
 
 
 // ----------------------------------------------------------------------
@@ -67,7 +68,7 @@
     private VideoRecorder           mVideoRecorder;
     private RVSensorLogger          mRVSensorLogger;
     private CoverageManager         mCoverManager;
-    private CameraPreviewer         mPreviewer;
+    private CameraContext mCameraContext;
 
     public static final int AXIS_NONE = 0;
     public static final int AXIS_ALL = SensorManager.AXIS_X +
@@ -99,7 +100,7 @@
         super.onPause();
         mController.quit();
 
-        mPreviewer.end();
+        mCameraContext.end();
         endSoundPool();
     }
 
@@ -128,8 +129,8 @@
      *
      */
     private void init() {
-        mPreviewer = new CameraPreviewer();
-        mPreviewer.init();
+        mCameraContext = new CameraContext();
+        mCameraContext.init();
 
         mCoverManager = new CoverageManager();
         mIndicatorView.setDataProvider(
@@ -140,7 +141,7 @@
         initSoundPool();
         mRVSensorLogger = new RVSensorLogger(this);
 
-        mVideoRecorder = new VideoRecorder(mPreviewer.getCamera());
+        mVideoRecorder = new VideoRecorder(mCameraContext.getCamera(), mCameraContext.getProfile());
 
         if (LOG_RAW_SENSORS) {
             mRawSensorLogger = new RawSensorLogger(mRecordDir);
@@ -173,7 +174,8 @@
         // X and Y
         final String axisName = "YXZ";
 
-        message("Manipulate the device in " + axisName.charAt(axis-1) + " axis (as illustrated) about the pattern.");
+        message("Manipulate the device in " + axisName.charAt(axis - 1) +
+                " axis (as illustrated) about the pattern.");
     }
 
     /**
@@ -250,20 +252,28 @@
      * Start the sensor recording
      */
     public void startRecordSensor() {
-        mRVSensorLogger.init();
-        if (LOG_RAW_SENSORS) {
-            mRawSensorLogger.init();
-        }
+        runOnUiThread(new Runnable() {
+            public void run() {
+                mRVSensorLogger.init();
+                if (LOG_RAW_SENSORS) {
+                    mRawSensorLogger.init();
+                }
+            }
+        });
     }
 
     /**
      * Stop the sensor recording
      */
     public void stopRecordSensor() {
-        mRVSensorLogger.end();
-        if (LOG_RAW_SENSORS) {
-            mRawSensorLogger.end();
-        }
+        runOnUiThread(new Runnable() {
+            public void run() {
+                mRVSensorLogger.end();
+                if (LOG_RAW_SENSORS) {
+                    mRawSensorLogger.end();
+                }
+            }
+        });
     }
 
     /**
@@ -365,20 +375,93 @@
     /**
      * Camera preview control class
      */
-    class CameraPreviewer {
+    class CameraContext {
         private Camera mCamera;
+        private CamcorderProfile mProfile;
+        private Camera.CameraInfo mCameraInfo;
 
-        CameraPreviewer() {
+        private int [] mPreferredProfiles = {
+                CamcorderProfile.QUALITY_480P,  // smaller -> faster
+                CamcorderProfile.QUALITY_720P,
+                CamcorderProfile.QUALITY_1080P,
+                CamcorderProfile.QUALITY_HIGH // existence guaranteed
+        };
+
+        CameraContext() {
             try {
-                mCamera = Camera.open(); // attempt to get a default Camera instance
+                mCamera = Camera.open(); // attempt to get a default Camera instance (0)
+                mProfile = null;
+                if (mCamera != null) {
+                    mCameraInfo = new Camera.CameraInfo();
+                    Camera.getCameraInfo(0, mCameraInfo);
+                    setupCamera();
+                }
             }
-            catch (Exception e) {
+            catch (Exception e){
                 // Camera is not available (in use or does not exist)
                 Log.e(TAG, "Cannot obtain Camera!");
             }
         }
 
         /**
+         * Find a preferred camera profile and set preview and picture size property accordingly.
+         */
+        void setupCamera() {
+            CamcorderProfile profile = null;
+            Camera.Parameters param = mCamera.getParameters();
+            List<Camera.Size> pre_sz = param.getSupportedPreviewSizes();
+            List<Camera.Size> pic_sz = param.getSupportedPictureSizes();
+
+            for (int i : mPreferredProfiles) {
+                if (CamcorderProfile.hasProfile(i)) {
+                    profile = CamcorderProfile.get(i);
+
+                    int valid = 0;
+                    for (Camera.Size j : pre_sz) {
+                        if (j.width == profile.videoFrameWidth &&
+                                j.height == profile.videoFrameHeight) {
+                            ++valid;
+                            break;
+                        }
+                    }
+                    for (Camera.Size j : pic_sz) {
+                        if (j.width == profile.videoFrameWidth &&
+                                j.height == profile.videoFrameHeight) {
+                            ++valid;
+                            break;
+                        }
+                    }
+                    if (valid == 2) {
+                        param.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
+                        param.setPictureSize(profile.videoFrameWidth, profile.videoFrameHeight);
+                        mCamera.setParameters(param);
+                        break;
+                    } else {
+                        profile = null;
+                    }
+                }
+            }
+            if (profile != null) {
+                float fovW = param.getHorizontalViewAngle();
+                float fovH = param.getVerticalViewAngle();
+                writeVideoMetaInfo(profile.videoFrameWidth, profile.videoFrameHeight,
+                        profile.videoFrameRate, fovW, fovH);
+            } else {
+                Log.e(TAG, "Cannot find a proper video profile");
+            }
+            mProfile = profile;
+
+        }
+
+
+        /**
+         * Get sensor information of the camera being used
+         */
+        public Camera.CameraInfo getCameraInfo() {
+            return mCameraInfo;
+        }
+
+        /**
          * Get the camera to be previewed
          * @return Reference to Camera used
          */
@@ -387,12 +470,20 @@
         }
 
         /**
+         * Get the camera profile to be used
+         * @return Reference to Camera profile
+         */
+        public CamcorderProfile getProfile() {
+            return mProfile;
+        }
+
+        /**
          * Setup the camera
          */
         public void init() {
             if (mCamera != null) {
                 double alpha = mCamera.getParameters().getHorizontalViewAngle()*Math.PI/180.0;
-                int width = 1920;
+                int width = mProfile.videoFrameWidth;
                 double fx = width/2/Math.tan(alpha/2.0);
 
                 if (LOCAL_LOGV) Log.v(TAG, "View angle="
@@ -400,7 +491,9 @@
 
                 RVCVCameraPreview cameraPreview =
                         (RVCVCameraPreview) findViewById(R.id.cam_preview);
-                cameraPreview.init(mCamera);
+                cameraPreview.init(mCamera,
+                        (float)mProfile.videoFrameWidth/mProfile.videoFrameHeight,
+                        mCameraInfo.orientation);
             } else {
                 message("Cannot open camera!");
                 finish();
@@ -466,26 +559,22 @@
     class VideoRecorder
     {
         private MediaRecorder mRecorder;
+        private CamcorderProfile mProfile;
         private Camera mCamera;
         private boolean mRunning = false;
 
-        private int [] mPreferredProfiles = {   CamcorderProfile.QUALITY_480P,  // smaller -> faster
-                                        CamcorderProfile.QUALITY_720P,
-                                        CamcorderProfile.QUALITY_1080P,
-                                        CamcorderProfile.QUALITY_HIGH // existence guaranteed
-                                    };
-
-
-        VideoRecorder(Camera camera) {
+        VideoRecorder(Camera camera, CamcorderProfile profile){
             mCamera = camera;
+            mProfile = profile;
         }
 
         /**
          * Initialize and start recording
          */
         public void init() {
-            float fovW =  mCamera.getParameters().getHorizontalViewAngle();
-            float fovH =  mCamera.getParameters().getVerticalViewAngle();
+            if (mCamera == null  || mProfile ==null){
+                return;
+            }
 
             mRecorder = new MediaRecorder();
             mCamera.unlock();
@@ -494,17 +583,7 @@
             mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
             mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
 
-            CamcorderProfile profile = null;
-            for (int i: mPreferredProfiles) {
-                if (CamcorderProfile.hasProfile(i)) {
-                    profile = CamcorderProfile.get(i);
-                    mRecorder.setProfile(profile);
-                    break;
-                }
-            }
-
-            writeVideoMetaInfo(profile.videoFrameWidth, profile.videoFrameHeight,
-                    profile.videoFrameRate, fovW, fovH);
+            mRecorder.setProfile(mProfile);
 
             try {
                 mRecorder.setOutputFile(getVideoRecFilePath());
@@ -689,8 +768,20 @@
          */
         public void init() {
             mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+            if (mSensorManager == null) {
+                Log.e(TAG,"SensorManager is null!");
+            }
             mRVSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
-            mSensorManager.registerListener(this, mRVSensor, SENSOR_RATE);
+            if (mRVSensor != null) {
+                if (LOCAL_LOGV) Log.v(TAG, "Got RV Sensor");
+            }else {
+                Log.e(TAG, "Did not get RV sensor");
+            }
+            if(mSensorManager.registerListener(this, mRVSensor, SENSOR_RATE)) {
+                if (LOCAL_LOGV) Log.v(TAG,"Register listener successfull");
+            } else {
+                Log.e(TAG,"Register listener failed");
+            }
 
             try {
                 mLogWriter= new OutputStreamWriter(
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
index 9afd1a9..3dc7270 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
@@ -15,12 +15,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import android.media.MediaCodec;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.os.Debug;
 import android.os.Environment;
+import android.os.PowerManager;
 import android.util.JsonWriter;
 import android.util.Log;
 
@@ -777,14 +777,16 @@
         VideoMetaInfo meta = new VideoMetaInfo(new File(mPath, "videometa.json"));
 
         int decimation = 1;
+        boolean use_timestamp = true;
 
+        // roughly determine if decimation is necessary
         if (meta.fps > DECIMATION_FPS_TARGET) {
             decimation = (int)(meta.fps / DECIMATION_FPS_TARGET);
             meta.fps /=decimation;
         }
 
         VideoDecoderForOpenCV videoDecoder = new VideoDecoderForOpenCV(
-                new File(mPath, "video.mp4"), decimation); // every 3 frame process 1 frame
+                new File(mPath, "video.mp4"), decimation);
 
 
         Mat frame;
@@ -820,12 +822,17 @@
         }
 
         long startTime = System.nanoTime();
+        long [] ts = new long[1];
 
-        while ((frame = videoDecoder.getFrame()) !=null) {
+        while ((frame = videoDecoder.getFrame(ts)) !=null) {
             if (LOCAL_LOGV) {
                 Log.v(TAG, "got a frame " + i);
             }
 
+            if (use_timestamp && ts[0] == -1) {
+                use_timestamp = false;
+            }
+
             // has to be in front, as there are cases where execution
             // will skip the later part of this while
             i++;
@@ -873,8 +880,16 @@
             // if error is reasonable, add it into the results
             if (error < REPROJECTION_THREASHOLD) {
                 double [] rv = new double[3];
+                double timestamp;
+
                 rvec.get(0,0, rv);
-                recs.add(new AttitudeRec((double) i / meta.fps, rodr2rpy(rv)));
+                if (use_timestamp) {
+                    timestamp = (double)ts[0] / 1e6;
+                } else {
+                    timestamp = (double) i / meta.fps;
+                }
+                if (LOCAL_LOGV) Log.v(TAG, String.format("Added frame %d  ts = %f", i, timestamp));
+                recs.add(new AttitudeRec(timestamp, rodr2rpy(rv)));
             }
 
             if (OUTPUT_DEBUG_IMAGE) {
@@ -906,6 +921,8 @@
      * One issue right now is that the glReadPixels is quite slow .. around 6.5ms for a 720p frame
      */
     private class VideoDecoderForOpenCV implements Runnable {
+        static final String TAG = "VideoDecoderForOpenCV";
+
         private MediaExtractor extractor=null;
         private MediaCodec decoder=null;
         private CtsMediaOutputSurface surface=null;
@@ -1031,7 +1048,7 @@
             }
 
             if (decoder == null) {
-                Log.e("VideoDecoderForOpenCV", "Can't find video info!");
+                Log.e(TAG, "Can't find video info!");
                 return;
             }
             valid = true;
@@ -1060,6 +1077,7 @@
             long timeoutUs = 10000;
 
             int iframe = 0;
+            long frameTimestamp = 0;
 
             while (!Thread.interrupted()) {
                 if (!isEOS) {
@@ -1076,8 +1094,12 @@
                                     MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                             isEOS = true;
                         } else {
-                            decoder.queueInputBuffer(inIndex, 0, sampleSize,
-                                    extractor.getSampleTime(), 0);
+                            frameTimestamp = extractor.getSampleTime();
+                            decoder.queueInputBuffer(inIndex, 0, sampleSize, frameTimestamp, 0);
+                            if (LOCAL_LOGD) {
+                                Log.d(TAG, String.format("Frame %d sample time %f s",
+                                            iframe, (double)frameTimestamp/1e6));
+                            }
                             extractor.advance();
                         }
                     }
@@ -1088,19 +1110,19 @@
                 switch (outIndex) {
                     case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                         if (LOCAL_LOGD) {
-                            Log.d("VideoDecoderForOpenCV", "INFO_OUTPUT_BUFFERS_CHANGED");
+                            Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
                         }
                         outputBuffers = decoder.getOutputBuffers();
                         break;
                     case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                         outFormat = decoder.getOutputFormat();
                         if (LOCAL_LOGD) {
-                            Log.d("VideoDecoderForOpenCV", "New format " + outFormat);
+                            Log.d(TAG, "New format " + outFormat);
                         }
                         break;
                     case MediaCodec.INFO_TRY_AGAIN_LATER:
                         if (LOCAL_LOGD) {
-                            Log.d("VideoDecoderForOpenCV", "dequeueOutputBuffer timed out!");
+                            Log.d(TAG, "dequeueOutputBuffer timed out!");
                         }
                         break;
                     default:
@@ -1118,12 +1140,12 @@
                         if (doRender) {
                             surface.awaitNewImage();
                             surface.drawImage();
-                            if (LOCAL_LOGD) {
-                                Log.d("VideoDecoderForOpenCV", "Finish drawing a frame!");
+                            if (LOCAL_LOGV) {
+                                Log.v(TAG, "Finish drawing a frame!");
                             }
                             if ((iframe++ % mDecimation) == 0) {
                                 //Send the frame for processing
-                                mMatBuffer.put();
+                                mMatBuffer.put(frameTimestamp);
                             }
                         }
                         break;
@@ -1149,8 +1171,8 @@
          * Get next valid frame
          * @return Frame in OpenCV mat
          */
-        public Mat getFrame() {
-            return mMatBuffer.get();
+        public Mat getFrame(long ts[]) {
+            return mMatBuffer.get(ts);
         }
 
         /**
@@ -1168,6 +1190,7 @@
             private Mat mat;
             private byte[] bytes;
             private ByteBuffer buf;
+            private long timestamp;
             private boolean full;
 
             private int mWidth, mHeight;
@@ -1180,6 +1203,7 @@
                 mat = new Mat(height, width, CvType.CV_8UC4); //RGBA
                 buf = ByteBuffer.allocateDirect(width*height*4);
                 bytes = new byte[width*height*4];
+                timestamp = -1;
 
                 mValid = true;
                 full = false;
@@ -1190,7 +1214,7 @@
                 notifyAll();
             }
 
-            public synchronized Mat get() {
+            public synchronized Mat get(long ts[]) {
 
                 if (!mValid) return null;
                 while (full == false) {
@@ -1204,9 +1228,11 @@
                 mat.put(0,0, bytes);
                 full = false;
                 notifyAll();
+                ts[0] = timestamp;
                 return mat;
             }
-            public synchronized void put() {
+
+            public synchronized void put(long ts) {
                 while (full) {
                     try {
                         wait();
@@ -1219,6 +1245,7 @@
                 buf.get(bytes);
                 buf.rewind();
 
+                timestamp = ts;
                 full = true;
                 notifyAll();
             }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
index 9a74a0e..35c4d56 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
@@ -16,9 +16,10 @@
 
 package com.android.cts.verifier.sensors;
 
-
+import android.content.Context;
 import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
 import android.os.Bundle;
+import android.os.PowerManager;
 
 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
 import com.android.cts.verifier.sensors.helpers.OpenCVLibrary;
@@ -82,7 +83,7 @@
 
         while(retry-->0) {
             try {
-                Thread.sleep(100);
+                Thread.sleep(250);
             } catch (InterruptedException e) {
                 //
             }
@@ -146,7 +147,15 @@
 
             // Analysis of recorded video and sensor data using RVCXAnalyzer
             RVCVXCheckAnalyzer analyzer = new RVCVXCheckAnalyzer(mRecPath);
+
+            // acquire a partial wake lock just in case CPU fall asleep
+            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "RVCVXCheckAnalyzer");
+
+            wl.acquire();
             mReport = analyzer.processDataSet();
+            wl.release();
 
             playSound();
             vibrate(500);
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)