Merge remote-tracking branch 'goog/mnc-cts-release' into HEAD
diff --git a/CtsBuild.mk b/CtsBuild.mk
index c745885..ba158ce 100644
--- a/CtsBuild.mk
+++ b/CtsBuild.mk
@@ -40,6 +40,10 @@
 	$(foreach executable,$(1),$(CTS_TESTCASES_OUT)/$(executable))
 endef
 
+define cts-get-deqp-api-test-xmls
+	$(foreach file,$(call find-files-in-subdirs, external/deqp/android/cts/master, 'com.drawelements.deqp.$(1).*xml', .),$(CTS_TESTCASES_OUT)/$(file))
+endef
+
 define cts-get-deqp-test-xmls
-	$(foreach api,$(1),$(CTS_TESTCASES_OUT)/com.drawelements.deqp.$(api).xml)
+	$(foreach api,$(1),$(call cts-get-deqp-api-test-xmls,$(api)))
 endef
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 4d30dda..975ac47 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -100,6 +100,7 @@
     CtsManagedProfileApp \
     CtsMonkeyApp \
     CtsMonkeyApp2 \
+    CtsPackageInstallerApp \
     CtsPermissionApp \
     CtsSimpleApp \
     CtsSimplePreMApp \
@@ -176,6 +177,7 @@
     CtsNdefTestCases \
     CtsNetTestCases \
     CtsNetTestCasesLegacyApi22 \
+    CtsNetTestCasesLegacyPermission22 \
     CtsOpenGLTestCases \
     CtsOpenGlPerfTestCases \
     CtsOsTestCases \
@@ -192,6 +194,7 @@
     CtsSecurityTestCases \
     CtsSignatureTestCases \
     CtsSpeechTestCases \
+    CtsSystemUiTestCases \
     CtsTelecomTestCases \
     CtsTelecomTestCases2 \
     CtsTelephonyTestCases \
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 756f959..835a4a4 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -448,8 +448,8 @@
 
         The out_surfaces field can specify the width(s), height(s), and
         format(s) of the captured image. The formats may be "yuv", "jpeg",
-        "dng", "raw", "raw10", or "raw12". The default is a YUV420 frame ("yuv")
-        corresponding to a full sensor frame.
+        "dng", "raw", "raw10", "raw12", or "rawStats". The default is a YUV420
+        frame ("yuv") corresponding to a full sensor frame.
 
         Note that one or more surfaces can be specified, allowing a capture to
         request images back in multiple formats (e.g.) raw+yuv, raw+jpeg,
@@ -536,6 +536,25 @@
             yuv_caps           = do_capture( [req1,req2], yuv_fmt           )
             yuv_caps, raw_caps = do_capture( [req1,req2], [yuv_fmt,raw_fmt] )
 
+        The "rawStats" format processes the raw image and returns a new image
+        of statistics from the raw image. The format takes additional keys,
+        "gridWidth" and "gridHeight" which are size of grid cells in a 2D grid
+        of the raw image. For each grid cell, the mean and variance of each raw
+        channel is computed, and the do_capture call returns two 4-element float
+        images of dimensions (rawWidth / gridWidth, rawHeight / gridHeight),
+        concatenated back-to-back, where the first iamge contains the 4-channel
+        means and the second contains the 4-channel variances.
+
+        For the rawStats format, if the gridWidth is not provided then the raw
+        image width is used as the default, and similarly for gridHeight. With
+        this, the following is an example of a output description that computes
+        the mean and variance across each image row:
+
+            {
+                "gridHeight": 1,
+                "format": "rawStats"
+            }
+
         Args:
             cap_request: The Python dict/list specifying the capture(s), which
                 will be converted to JSON and sent to the device.
@@ -550,7 +569,8 @@
             * data: the image data as a numpy array of bytes.
             * width: the width of the captured image.
             * height: the height of the captured image.
-            * format: image the format, in ["yuv","jpeg","raw","raw10","dng"].
+            * format: image the format, in [
+                        "yuv","jpeg","raw","raw10","raw12","rawStats","dng"].
             * metadata: the capture result object (Python dictionary).
         """
         cmd = {}
@@ -577,9 +597,13 @@
         nsurf = 1 if out_surfaces is None else len(cmd["outputSurfaces"])
         if len(formats) > len(set(formats)):
             raise its.error.Error('Duplicate format requested')
-        if "dng" in formats and "raw" in formats or \
-                "dng" in formats and "raw10" in formats or \
-                "raw" in formats and "raw10" in formats:
+        raw_formats = 0;
+        raw_formats += 1 if "dng" in formats else 0
+        raw_formats += 1 if "raw" in formats else 0
+        raw_formats += 1 if "raw10" in formats else 0
+        raw_formats += 1 if "raw12" in formats else 0
+        raw_formats += 1 if "rawStats" in formats else 0
+        if raw_formats > 1:
             raise its.error.Error('Different raw formats not supported')
 
         # Detect long exposure time and set timeout accordingly
@@ -603,14 +627,16 @@
         # the burst, however individual images of different formats can come
         # out in any order for that capture.
         nbufs = 0
-        bufs = {"yuv":[], "raw":[], "raw10":[], "dng":[], "jpeg":[]}
+        bufs = {"yuv":[], "raw":[], "raw10":[], "raw12":[],
+                "rawStats":[], "dng":[], "jpeg":[]}
         mds = []
         widths = None
         heights = None
         while nbufs < ncap*nsurf or len(mds) < ncap:
             jsonObj,buf = self.__read_response_from_socket()
             if jsonObj['tag'] in ['jpegImage', 'yuvImage', 'rawImage', \
-                    'raw10Image', 'dngImage'] and buf is not None:
+                    'raw10Image', 'raw12Image', 'rawStatsImage', 'dngImage'] \
+                    and buf is not None:
                 fmt = jsonObj['tag'][:-5]
                 bufs[fmt].append(buf)
                 nbufs += 1
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index ea01a3e..a5ac60b 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -81,6 +81,25 @@
     else:
         raise its.error.Error('Invalid format %s' % (cap["format"]))
 
+def unpack_rawstats_capture(cap):
+    """Unpack a rawStats capture to the mean and variance images.
+
+    Args:
+        cap: A capture object as returned by its.device.do_capture.
+
+    Returns:
+        Tuple (mean_image var_image) of float-4 images, with non-normalized
+        pixel values computed from the RAW16 images on the device
+    """
+    assert(cap["format"] == "rawStats")
+    w = cap["width"]
+    h = cap["height"]
+    img = numpy.ndarray(shape=(2*h*w*4,), dtype='<f', buffer=cap["data"])
+    analysis_image = img.reshape(2,h,w,4)
+    mean_image = analysis_image[0,:,:,:].reshape(h,w,4)
+    var_image = analysis_image[1,:,:,:].reshape(h,w,4)
+    return mean_image, var_image
+
 def unpack_raw10_capture(cap, props):
     """Unpack a raw-10 capture to a raw-16 capture.
 
@@ -604,6 +623,21 @@
         variances.append(numpy.var(img[:,:,i], dtype=numpy.float64))
     return variances
 
+def compute_image_snrs(img):
+    """Calculate the SNR (db) of each color channel in the image.
+
+    Args:
+        img: Numpy float image array, with pixel values in [0,1].
+
+    Returns:
+        A list of SNR value, one per color channel in the image.
+    """
+    means = compute_image_means(img)
+    variances = compute_image_variances(img)
+    std_devs = [math.sqrt(v) for v in variances]
+    snr = [20 * math.log10(m/s) for m,s in zip(means, std_devs)]
+    return snr
+
 def write_image(img, fname, apply_gamma=False):
     """Save a float-3 numpy array image to a file.
 
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index 82346ec..ac384fb 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -152,24 +152,37 @@
 
     return req
 
-def get_available_output_sizes(fmt, props):
+def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
     """Return a sorted list of available output sizes for a given format.
 
     Args:
         fmt: the output format, as a string in
             ["jpg", "yuv", "raw", "raw10", "raw12"].
         props: the object returned from its.device.get_camera_properties().
+        max_size: (Optional) A (w,h) tuple.
+            Sizes larger than max_size (either w or h)  will be discarded.
+        match_ar_size: (Optional) A (w,h) tuple.
+            Sizes not matching the aspect ratio of match_ar_size will be
+            discarded.
 
     Returns:
         A sorted list of (w,h) tuples (sorted large-to-small).
     """
-    fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26, "yuv":0x23,
+    AR_TOLERANCE = 0.03
+    fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
                  "jpg":0x100, "jpeg":0x100}
     configs = props['android.scaler.streamConfigurationMap']\
                    ['availableStreamConfigurations']
     fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
     out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
     out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
+    if max_size:
+        out_sizes = [s for s in out_sizes if
+                s[0] <= max_size[0] and s[1] <= max_size[1]]
+    if match_ar_size:
+        ar = match_ar_size[0] / float(match_ar_size[1])
+        out_sizes = [s for s in out_sizes if
+                abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
     out_sizes.sort(reverse=True)
     return out_sizes
 
diff --git a/apps/CameraITS/tests/inprog/test_rawstats.py b/apps/CameraITS/tests/inprog/test_rawstats.py
new file mode 100644
index 0000000..8083f0b
--- /dev/null
+++ b/apps/CameraITS/tests/inprog/test_rawstats.py
@@ -0,0 +1,48 @@
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import its.image
+import its.caps
+import its.device
+import its.objects
+import its.target
+import os.path
+import math
+
+def main():
+    """Test capturing some rawstats data.
+    """
+    NAME = os.path.basename(__file__).split(".")[0]
+
+    with its.device.ItsSession() as cam:
+
+        cam.do_3a(do_af=False);
+        req = its.objects.auto_capture_request()
+
+        for (gw,gh) in [(16,16)]:#,(4080,1)]:
+            cap = cam.do_capture(req,
+                {"format":"rawStats","gridWidth":gw,"gridHeight":gh})
+            mean_image, var_image = its.image.unpack_rawstats_capture(cap)
+
+            if gw > 1 and gh > 1:
+                h,w,_ = mean_image.shape
+                for ch in range(4):
+                    m = mean_image[:,:,ch].reshape(h,w,1)/1023.0
+                    v = var_image[:,:,ch].reshape(h,w,1)
+                    its.image.write_image(m, "%s_mean_ch%d.jpg" % (NAME,ch), True)
+                    its.image.write_image(v, "%s_var_ch%d.jpg" % (NAME,ch), True)
+
+if __name__ == '__main__':
+    main()
+
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1/test_exposure.py
index d217bdb..89bc724 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1/test_exposure.py
@@ -35,12 +35,16 @@
     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 = []
     g_means = []
     b_means = []
+    threshold_max_level_diff = THRESHOLD_MAX_LEVEL_DIFF
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -48,27 +52,44 @@
                              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=%02d.jpg" % (NAME, m))
+            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])
-            m = m + 4
+            # 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)
+
+        # Allow more threshold for devices with wider exposure range
+        if m >= 64.0:
+            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))
 
@@ -83,7 +104,7 @@
         max_diff = max_val - min_val
         print "Channel %d line fit (y = mx+b): m = %f, b = %f" % (chan, m, b)
         print "Channel max %f min %f diff %f" % (max_val, min_val, max_diff)
-        assert(max_diff < THRESHOLD_MAX_LEVEL_DIFF)
+        assert(max_diff < threshold_max_level_diff)
         assert(b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL)
         for v in values:
             assert(v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL)
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index 2c8d73b..1072684 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -35,13 +35,13 @@
     """
     NAME = os.path.basename(__file__).split(".")[0]
 
-    RELATIVE_ERROR_TOLERANCE = 0.1
+    NUM_SAMPLES_PER_MODE = 4
+    SNR_TOLERANCE = 3 # unit in db
+    # List of SNRs for R,G,B.
+    snrs = [[], [], []]
 
-    # List of variances for Y,U,V.
-    variances = [[],[],[]]
-
-    # Reference (baseline) variance for each of Y,U,V.
-    ref_variance = []
+    # Reference (baseline) SNR for each of R,G,B.
+    ref_snr = []
 
     nr_modes_reported = []
 
@@ -56,74 +56,89 @@
         req = its.objects.manual_capture_request(s, e)
         req["android.noiseReduction.mode"] = 0
         cap = cam.do_capture(req)
+        rgb_image = its.image.convert_capture_to_rgb_image(cap)
         its.image.write_image(
-                its.image.convert_capture_to_rgb_image(cap),
+                rgb_image,
                 "%s_low_gain.jpg" % (NAME))
-        planes = its.image.convert_capture_to_planes(cap)
-        for j in range(3):
-            img = planes[j]
-            tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-            ref_variance.append(its.image.compute_image_variances(tile)[0])
-        print "Ref variances:", ref_variance
+        rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
+        ref_snr = its.image.compute_image_snrs(rgb_tile)
+        print "Ref SNRs:", ref_snr
 
+        e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
         # NR modes 0, 1, 2, 3, 4 with high gain
         for mode in range(5):
             # Skip unavailable modes
             if not its.caps.noise_reduction_mode(props, mode):
                 nr_modes_reported.append(mode)
                 for channel in range(3):
-                    variances[channel].append(0)
+                    snrs[channel].append(0)
                 continue;
 
-            e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
-            req = its.objects.manual_capture_request(s, e)
-            req["android.noiseReduction.mode"] = mode
-            cap = cam.do_capture(req)
-            nr_modes_reported.append(
-                    cap["metadata"]["android.noiseReduction.mode"])
-            its.image.write_image(
-                    its.image.convert_capture_to_rgb_image(cap),
-                    "%s_high_gain_nr=%d.jpg" % (NAME, mode))
-            planes = its.image.convert_capture_to_planes(cap)
-            for j in range(3):
-                img = planes[j]
-                tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-                variance = its.image.compute_image_variances(tile)[0]
-                variances[j].append(variance / ref_variance[j])
-        print "Variances with NR mode [0,1,2,3,4]:", variances
+            rgb_snr_list = []
+            # Capture several images to account for per frame noise variations
+            for n in range(NUM_SAMPLES_PER_MODE):
+                req = its.objects.manual_capture_request(s, e)
+                req["android.noiseReduction.mode"] = mode
+                cap = cam.do_capture(req)
+                rgb_image = its.image.convert_capture_to_rgb_image(cap)
+                if n == 0:
+                    nr_modes_reported.append(
+                            cap["metadata"]["android.noiseReduction.mode"])
+                    its.image.write_image(
+                            rgb_image,
+                            "%s_high_gain_nr=%d.jpg" % (NAME, mode))
+                rgb_tile = its.image.get_image_patch(
+                        rgb_image, 0.45, 0.45, 0.1, 0.1)
+                rgb_snrs = its.image.compute_image_snrs(rgb_tile)
+                rgb_snr_list.append(rgb_snrs)
+
+            r_snrs = [rgb[0] for rgb in rgb_snr_list]
+            g_snrs = [rgb[1] for rgb in rgb_snr_list]
+            b_snrs = [rgb[2] for rgb in rgb_snr_list]
+            rgb_snrs = [numpy.mean(r_snrs), numpy.mean(g_snrs), numpy.mean(b_snrs)]
+            print "NR mode", mode, "SNRs:"
+            print "    R SNR:", rgb_snrs[0],\
+                    "Min:", min(r_snrs), "Max:", max(r_snrs)
+            print "    G SNR:", rgb_snrs[1],\
+                    "Min:", min(g_snrs), "Max:", max(g_snrs)
+            print "    B SNR:", rgb_snrs[2],\
+                    "Min:", min(b_snrs), "Max:", max(b_snrs)
+
+            for chan in range(3):
+                snrs[chan].append(rgb_snrs[chan])
 
     # Draw a plot.
     for j in range(3):
-        pylab.plot(range(5), variances[j], "rgb"[j])
-    matplotlib.pyplot.savefig("%s_plot_variances.png" % (NAME))
+        pylab.plot(range(5), snrs[j], "rgb"[j])
+    matplotlib.pyplot.savefig("%s_plot_SNRs.png" % (NAME))
 
     assert(nr_modes_reported == [0,1,2,3,4])
 
     for j in range(3):
-        # Smaller variance is better
+        # Larger SNR is better
         # Verify OFF(0) is not better than FAST(1)
-        assert(variances[j][0] >
-               variances[j][1] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+        assert(snrs[j][0] <
+               snrs[j][1] + SNR_TOLERANCE)
         # Verify FAST(1) is not better than HQ(2)
-        assert(variances[j][1] >
-               variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+        assert(snrs[j][1] <
+               snrs[j][2] + SNR_TOLERANCE)
         # Verify HQ(2) is better than OFF(0)
-        assert(variances[j][0] > variances[j][2])
+        assert(snrs[j][0] < snrs[j][2])
         if its.caps.noise_reduction_mode(props, 3):
             # Verify OFF(0) is not better than MINIMAL(3)
-            assert(variances[j][0] >
-                   variances[j][3] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+            assert(snrs[j][0] <
+                   snrs[j][3] + SNR_TOLERANCE)
             # Verify MINIMAL(3) is not better than HQ(2)
-            assert(variances[j][3] >
-                   variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+            assert(snrs[j][3] <
+                   snrs[j][2] + SNR_TOLERANCE)
             if its.caps.noise_reduction_mode(props, 4):
                 # Verify ZSL(4) is close to MINIMAL(3)
-                assert(numpy.isclose(variances[j][4], variances[j][3],
-                                     RELATIVE_ERROR_TOLERANCE))
+                assert(numpy.isclose(snrs[j][4], snrs[j][3],
+                                     atol=SNR_TOLERANCE))
         elif its.caps.noise_reduction_mode(props, 4):
             # Verify ZSL(4) is close to OFF(0)
-            assert(numpy.isclose(variances[j][4], variances[j][0],
-                                 RELATIVE_ERROR_TOLERANCE))
+            assert(numpy.isclose(snrs[j][4], snrs[j][0],
+                                 atol=SNR_TOLERANCE))
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
index 6c2b5c1..e176312 100644
--- a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
@@ -44,6 +44,8 @@
 
         # Expose for the scene with min sensitivity
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+        # Digital gains might not be visible on RAW data
+        sens_max = props['android.sensor.maxAnalogSensitivity']
         sens_step = (sens_max - sens_min) / NUM_STEPS
         s_ae,e_ae,_,_,_  = cam.do_3a(get_results=True)
         s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
index 14c5eb0..cc0ce14 100644
--- a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
@@ -42,6 +42,8 @@
 
         # Expose for the scene with min sensitivity
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+        # Digital gains might not be visible on RAW data
+        sens_max = props['android.sensor.maxAnalogSensitivity']
         sens_step = (sens_max - sens_min) / NUM_STEPS
         s_ae,e_ae,_,_,_  = cam.do_3a(get_results=True)
         s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
index 757dfeb..f0a6fbe 100644
--- a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
@@ -38,7 +38,8 @@
 
     NAME = os.path.basename(__file__).split(".")[0]
 
-    RELATIVE_ERROR_TOLERANCE = 0.1
+    NUM_SAMPLES_PER_MODE = 4
+    SNR_TOLERANCE = 3 # unit in db
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -60,7 +61,7 @@
 
         for reprocess_format in reprocess_formats:
             # List of variances for R, G, B.
-            variances = []
+            snrs = [[], [], []]
             nr_modes_reported = []
 
             # NR mode 0 with low gain
@@ -77,71 +78,90 @@
             img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
             its.image.write_image(img, "%s_low_gain_fmt=jpg.jpg" % (NAME))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-            ref_variance = its.image.compute_image_variances(tile)
-            print "Ref variances:", ref_variance
+            ref_snr = its.image.compute_image_snrs(tile)
+            print "Ref SNRs:", ref_snr
 
+            e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
             for nr_mode in range(5):
                 # Skip unavailable modes
                 if not its.caps.noise_reduction_mode(props, nr_mode):
                     nr_modes_reported.append(nr_mode)
-                    variances.append(0)
+                    for channel in range(3):
+                        snrs[channel].append(0)
                     continue
 
-                # NR modes with high gain
-                e, s = its.target.get_target_exposure_combos(cam) \
-                    ["maxSensitivity"]
-                req = its.objects.manual_capture_request(s, e)
-                req["android.noiseReduction.mode"] = nr_mode
-                cap = cam.do_capture(req, out_surface, reprocess_format)
-                nr_modes_reported.append(
-                    cap["metadata"]["android.noiseReduction.mode"])
+                rgb_snr_list = []
+                # Capture several images to account for per frame noise
+                # variations
+                for n in range(NUM_SAMPLES_PER_MODE):
+                    req = its.objects.manual_capture_request(s, e)
+                    req["android.noiseReduction.mode"] = nr_mode
+                    cap = cam.do_capture(req, out_surface, reprocess_format)
 
-                img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
-                its.image.write_image(
-                    img, "%s_high_gain_nr=%d_fmt=jpg.jpg" % (NAME, nr_mode))
-                tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-                # Get the variances for R, G, and B channels
-                variance = its.image.compute_image_variances(tile)
-                variances.append(
-                    [variance[chan] / ref_variance[chan] for chan in range(3)])
-            print "Variances with NR mode [0,1,2,3,4]:", variances
+                    img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+                    if n == 0:
+                        its.image.write_image(
+                                img,
+                                "%s_high_gain_nr=%d_fmt=jpg.jpg"
+                                        %(NAME, nr_mode))
+                        nr_modes_reported.append(
+                                cap["metadata"]["android.noiseReduction.mode"])
+
+                    tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+                    # Get the variances for R, G, and B channels
+                    rgb_snrs = its.image.compute_image_snrs(tile)
+                    rgb_snr_list.append(rgb_snrs)
+
+                r_snrs = [rgb[0] for rgb in rgb_snr_list]
+                g_snrs = [rgb[1] for rgb in rgb_snr_list]
+                b_snrs = [rgb[2] for rgb in rgb_snr_list]
+                rgb_snrs = [numpy.mean(r_snrs),
+                            numpy.mean(g_snrs),
+                            numpy.mean(b_snrs)]
+                print "NR mode", nr_mode, "SNRs:"
+                print "    R SNR:", rgb_snrs[0],\
+                        "Min:", min(r_snrs), "Max:", max(r_snrs)
+                print "    G SNR:", rgb_snrs[1],\
+                        "Min:", min(g_snrs), "Max:", max(g_snrs)
+                print "    B SNR:", rgb_snrs[2],\
+                        "Min:", min(b_snrs), "Max:", max(b_snrs)
+
+                for chan in range(3):
+                    snrs[chan].append(rgb_snrs[chan])
 
             # Draw a plot.
-            for chan in range(3):
-                line = []
-                for nr_mode in range(5):
-                    line.append(variances[nr_mode][chan])
-                pylab.plot(range(5), line, "rgb"[chan])
+            for channel in range(3):
+                pylab.plot(range(5), snrs[channel], "rgb"[channel])
 
-            matplotlib.pyplot.savefig("%s_plot_%s_variances.png" %
+            matplotlib.pyplot.savefig("%s_plot_%s_SNRs.png" %
                                       (NAME, reprocess_format))
 
             assert(nr_modes_reported == [0,1,2,3,4])
 
             for j in range(3):
-                # Smaller variance is better
+                # Larger is better
                 # Verify OFF(0) is not better than FAST(1)
-                assert(variances[0][j] >
-                       variances[1][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                assert(snrs[j][0] <
+                       snrs[j][1] + SNR_TOLERANCE)
                 # Verify FAST(1) is not better than HQ(2)
-                assert(variances[1][j] >
-                       variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                assert(snrs[j][1] <
+                       snrs[j][2] + SNR_TOLERANCE)
                 # Verify HQ(2) is better than OFF(0)
-                assert(variances[0][j] > variances[2][j])
+                assert(snrs[j][0] < snrs[j][2])
                 if its.caps.noise_reduction_mode(props, 3):
                     # Verify OFF(0) is not better than MINIMAL(3)
-                    assert(variances[0][j] >
-                           variances[3][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                    assert(snrs[j][0] <
+                           snrs[j][3] + SNR_TOLERANCE)
                     # Verify MINIMAL(3) is not better than HQ(2)
-                    assert(variances[3][j] >
-                           variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+                    assert(snrs[j][3] <
+                           snrs[j][2] + SNR_TOLERANCE)
                     # Verify ZSL(4) is close to MINIMAL(3)
-                    assert(numpy.isclose(variances[4][j], variances[3][j],
-                                         RELATIVE_ERROR_TOLERANCE))
+                    assert(numpy.isclose(snrs[j][4], snrs[j][3],
+                                         atol=SNR_TOLERANCE))
                 else:
                     # Verify ZSL(4) is close to OFF(0)
-                    assert(numpy.isclose(variances[4][j], variances[0][j],
-                                         RELATIVE_ERROR_TOLERANCE))
+                    assert(numpy.isclose(snrs[j][4], snrs[j][0],
+                                         atol=SNR_TOLERANCE))
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
index 33e7763..268b64a 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
@@ -31,6 +31,12 @@
         cam.do_3a()
 
         req = its.objects.auto_capture_request()
+        max_dng_size = \
+                its.objects.get_available_output_sizes("raw", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_dng_size)[0]
+        out_surfaces = [{"format":"dng"},
+                        {"format":"yuv", "width":w, "height":h}]
         cap_dng, cap_yuv = cam.do_capture(req, cam.CAP_DNG_YUV)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
index 9ce8d76..78378eb 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
@@ -27,13 +27,17 @@
 
     THRESHOLD_MAX_RMS_DIFF = 0.01
 
-    fmt_yuv =  {"format":"yuv"}
-    fmt_jpeg = {"format":"jpeg"}
-
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         its.caps.skip_unless(its.caps.compute_target_exposure(props))
 
+        max_jpeg_size = \
+                its.objects.get_available_output_sizes("jpeg", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_jpeg_size)[0]
+        fmt_yuv =  {"format":"yuv", "width":w, "height":h}
+        fmt_jpeg = {"format":"jpeg"}
+
         # Use a manual request with a linear tonemap so that the YUV and JPEG
         # should look the same (once converted by the its.image module).
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index f13801b..bfa6a28 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -38,7 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True, props)
 
-        cap_raw, cap_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
+        max_raw_size = \
+                its.objects.get_available_output_sizes("raw", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw_size)[0]
+        out_surfaces = [{"format":"raw"},
+                        {"format":"yuv", "width":w, "height":h}]
+        cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index e52946d..322af10 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -38,8 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True, props)
 
+        max_raw10_size = \
+                its.objects.get_available_output_sizes("raw10", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw10_size)[0]
         cap_raw, cap_yuv = cam.do_capture(req,
-                [{"format":"raw10"}, {"format":"yuv"}])
+                [{"format":"raw10"},
+                 {"format":"yuv", "width":w, "height":h}])
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
index c5c3c73..b3cca0b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
@@ -38,8 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True, props)
 
+        max_raw12_size = \
+                its.objects.get_available_output_sizes("raw12", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw12_size)[0]
         cap_raw, cap_yuv = cam.do_capture(req,
-                [{"format":"raw12"}, {"format":"yuv"}])
+                [{"format":"raw12"},
+                 {"format":"yuv", "width":w, "height":h}])
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index 73834cb..e96a9ee 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -53,22 +53,28 @@
     """
 
     NAME = os.path.basename(__file__).split(".")[0]
+    NUM_SAMPLES = 4
 
     req = its.objects.manual_capture_request(sensitivity, exp)
     req["android.lens.focusDistance"] = fd
     req["android.edge.mode"] = edge_mode
     if (reprocess_format != None):
         req["android.reprocess.effectiveExposureFactor"] = 1.0
-    cap = cam.do_capture(req, out_surface, reprocess_format)
 
-    img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
-    its.image.write_image(img, "%s_edge=%d_reprocess_fmt_%s.jpg" %
-        (NAME, edge_mode, reprocess_format))
-    tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+    sharpness_list = []
+    for n in range(NUM_SAMPLES):
+        cap = cam.do_capture(req, out_surface, reprocess_format)
+        img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+        if n == 0:
+            its.image.write_image(img, "%s_reprocess_fmt_%s_edge=%d.jpg" %
+                (NAME, reprocess_format, edge_mode))
+            res_edge_mode = cap["metadata"]["android.edge.mode"]
+        tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+        sharpness_list.append(its.image.compute_image_sharpness(tile))
 
     ret = {}
-    ret["edge_mode"] = cap["metadata"]["android.edge.mode"]
-    ret["sharpness"] = its.image.compute_image_sharpness(tile)
+    ret["edge_mode"] = res_edge_mode
+    ret["sharpness"] = numpy.mean(sharpness_list)
 
     return ret
 
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 8cec7ea..34246cc 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -41,7 +41,7 @@
 
 LOCAL_PACKAGE_NAME := CtsVerifier
 
-LOCAL_AAPT_FLAGS += --version-name "6.0_r0 $(BUILD_NUMBER)"
+LOCAL_AAPT_FLAGS += --version-name "6.0_r1 $(BUILD_NUMBER)"
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni libaudioloopback_jni
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 217913c..5e8ab901 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,8 +1459,87 @@
 
         <activity android:name=".managedprovisioning.CrossProfileTestActivity">
             <intent-filter>
-                <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE" />
-                <category android:name="android.intent.category.DEFAULT"></category>
+                <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" />
+                <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>
 
@@ -1570,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>
@@ -1609,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>
@@ -1641,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 c5469a7..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,16 +1468,39 @@
         \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.
     </string>
     <string name="provisioning_byod_keyguard_disabled_features_instruction">
-        Please press the \"Prepare test\" button to disable trust agents.\n
+        Please go to Settings &gt; Security &gt; Device administrators and set
+        \"CTS Verifier - AfW Admin\" as active admin.\n
+        After that please press the \"Prepare test\" button to disable trust agents.\n
         Then please press through the following verification steps.\n
-        Note: Device password will be set to \"testpassword\". After leaving the screen device password be cleared.
+        Note: Device password will be set to \"testpassword\". After leaving the screen device
+        password and active admin status will be cleared.
     </string>
     <string name="provisioning_byod_keyguard_disabled_features_prepare_button">Prepare test</string>
+    <string name="provisioning_byod_keyguard_disabled_features_not_admin">CtsVerifier is not active admin. Please follow instructions.</string>
     <string name="provisioning_byod_disable_trust_agents">Disable trust agents</string>
     <string name="provisioning_byod_disable_trust_agents_instruction">
         Please press the Go button to go to Settings > Security. Then go to Trusted agents and\n
@@ -1525,6 +1567,10 @@
         - Both Personal and Work categories exist.\n
         - \"Remove work profile\" exists under the Work category.\n
         \n
+        Furthermore, hit the action overflow button (3 dots) and verify that:\n
+        - There are two auto-sync options present, one for personal and one for work data.\n
+        - De-selecting either option prompts a warning dialog.\n
+        \n
         Use the Back button to return to this page.
     </string>
     <string name="provisioning_byod_admin_visible_instruction">
@@ -1632,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>
@@ -1867,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">
@@ -1956,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>
 
@@ -1976,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>
@@ -2005,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">
@@ -2047,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 3fec414..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;
@@ -160,6 +173,8 @@
                             IntentFiltersTestHelper.FLAG_INTENTS_FROM_MANAGED);
             setResult(intentFiltersSetForManagedIntents? RESULT_OK : RESULT_FAILED, null);
         } else if (action.equals(ACTION_CAPTURE_AND_CHECK_IMAGE)) {
+            // We need the camera permission to send the image capture intent.
+            grantCameraPermissionToSelf();
             Intent captureImageIntent = getCaptureImageIntent();
             mImageUri = getTempUri("image.jpg");
             captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
@@ -172,6 +187,8 @@
             }
             return;
         } else if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO)) {
+            // We need the camera permission to send the video capture intent.
+            grantCameraPermissionToSelf();
             Intent captureVideoIntent = getCaptureVideoIntent();
             mVideoUri = getTempUri("video.mp4");
             captureVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri);
@@ -207,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();
@@ -336,6 +363,19 @@
         Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
     }
 
+    private void grantCameraPermissionToSelf() {
+        mDevicePolicyManager.setPermissionGrantState(mAdminReceiverComponent, getPackageName(),
+                android.Manifest.permission.CAMERA,
+                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 579cbcc..eef6299 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
+import android.app.DownloadManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -29,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;
@@ -38,6 +40,7 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -50,131 +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(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(MediaStore.Audio.Media.RECORD_SOUND_ACTION),
-        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_MANAGE_APPLICATIONS_SETTINGS),
-        new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS),
-        new Intent(Settings.ACTION_APPLICATION_SETTINGS),
-        new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
-                Uri.parse("package:com.android.chrome")),
-        new Intent("android.settings.ACTION_OTHER_SOUND_SETTINGS"),
-        new Intent(Settings.ACTION_WIFI_IP_SETTINGS),
-        new Intent(Settings.ACTION_WIFI_SETTINGS),
-        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)
-    };
+    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;
@@ -185,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) {
@@ -296,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,
@@ -306,4 +379,4 @@
         }
         return null;
     }
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
index 0fdc498..1a158f8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.fingerprint.FingerprintManager;
@@ -25,6 +26,7 @@
 import android.provider.Settings;
 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;
@@ -32,6 +34,8 @@
 
 public class KeyguardDisabledFeaturesActivity extends DialogTestListActivity {
 
+    private DevicePolicyManager mDpm;
+
     public KeyguardDisabledFeaturesActivity() {
         super(R.layout.provisioning_byod,
                 R.string.provisioning_byod_keyguard_disabled_features,
@@ -43,12 +47,20 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+
         mPrepareTestButton.setText(
                 R.string.provisioning_byod_keyguard_disabled_features_prepare_button);
         mPrepareTestButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    resetPassword("testpassword");
+                    if (!mDpm.isAdminActive(DeviceAdminTestReceiver.getReceiverComponentName())) {
+                        Toast.makeText(KeyguardDisabledFeaturesActivity.this,
+                                R.string.provisioning_byod_keyguard_disabled_features_not_admin,
+                                Toast.LENGTH_SHORT).show();
+                        return;
+                    }
+                    mDpm.resetPassword("testpassword", 0);
                     setKeyguardDisabledFeatures(DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS |
                             DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT |
                             DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
@@ -60,7 +72,11 @@
     public void finish() {
         // Pass and fail buttons are known to call finish() when clicked, and this is when we want to
         // clear the password.
-        resetPassword(null);
+        final ComponentName adminComponent = DeviceAdminTestReceiver.getReceiverComponentName();
+        if (mDpm.isAdminActive(adminComponent)) {
+            mDpm.resetPassword(null, 0);
+            mDpm.removeActiveAdmin(adminComponent);
+        }
         super.finish();
     }
 
@@ -71,16 +87,6 @@
         startActivity(setKeyguardDisabledFeaturesIntent);
     }
 
-    /**
-     * Reset device password
-     * @param password password to reset to (may be null)
-     */
-    private void resetPassword(String password) {
-        DevicePolicyManager dpm = (DevicePolicyManager)
-                getSystemService(Context.DEVICE_POLICY_SERVICE);
-        dpm.resetPassword(password, 0);
-    }
-
     @Override
     protected void setupTests(ArrayTestListAdapter adapter) {
         adapter.add(new DialogTestListItem(this, R.string.provisioning_byod_disable_trust_agents,
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 f8f1a9a..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);
     }
 
@@ -302,8 +304,9 @@
      * It cannot be reused.
      */
     private class TriggerVerifier extends TriggerEventListener {
-        private volatile CountDownLatch mCountDownLatch = new CountDownLatch(1);
+        private volatile CountDownLatch mCountDownLatch;
         private volatile TriggerEventRegistry mEventRegistry;
+        private volatile long mTimestampForTriggeredEvent = 0;
 
         // TODO: refactor out if needed
         private class TriggerEventRegistry {
@@ -329,10 +332,7 @@
         }
 
         public long getTimeStampForTriggerEvent() {
-            if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
-                return mEventRegistry.triggerEvent.timestamp;
-            }
-            return 0;
+            return mTimestampForTriggeredEvent;
         }
 
         public String verifyEventTriggered() throws Throwable {
@@ -388,9 +388,16 @@
         }
 
         private TriggerEventRegistry awaitForEvent() throws InterruptedException {
+            mCountDownLatch = new CountDownLatch(1);
             mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
             TriggerEventRegistry registry = mEventRegistry;
 
+            // Save the last timestamp when the event triggered.
+            if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
+                mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp;
+            }
+
+            mEventRegistry = null;
             playSound();
             return registry != null ? registry : new TriggerEventRegistry(null, 0);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
new file mode 100644
index 0000000..43f293a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.tv.TvContract;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Tests for verifying TV app behavior for TV app-link.
+ */
+public class AppLinkTestActivity extends TvAppVerifierActivity implements View.OnClickListener {
+    private static final long TIMEOUT_MS = 5l * 60l * 1000l;  // 5 mins.
+
+    private boolean mSelectAppLinkItemPassed;
+    private View mSelectAppLinkItem;
+    private View mVerifyAppLinkIntentItem;
+    private View mVerifyAppLinkCardItem;
+
+    Runnable mSelectAppLinkFailCallback;
+
+    @Override
+    public void onClick(View v) {
+        final View postTarget = getPostTarget();
+
+        if (containsButton(mSelectAppLinkItem, v)) {
+            Intent tvAppIntent = null;
+            String[] projection = { TvContract.Channels._ID };
+            try (Cursor cursor = getContentResolver().query(
+                    TvContract.buildChannelsUriForInput(MockTvInputService.getInputId(this)),
+                    projection, null, null, null)) {
+                if (cursor != null && cursor.moveToNext()) {
+                    tvAppIntent = new Intent(Intent.ACTION_VIEW,
+                            TvContract.buildChannelUri(cursor.getLong(0)));
+                }
+            }
+            if (tvAppIntent == null) {
+                Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+                return;
+            }
+
+            mSelectAppLinkFailCallback = new Runnable() {
+                @Override
+                public void run() {
+                    mSelectAppLinkItemPassed = false;
+                    setPassState(mSelectAppLinkItem, false);
+                    setPassState(mVerifyAppLinkIntentItem, false);
+                }
+            };
+            postTarget.postDelayed(mSelectAppLinkFailCallback, TIMEOUT_MS);
+            mSelectAppLinkItemPassed = true;
+            setPassState(mSelectAppLinkItem, true);
+
+            startActivity(tvAppIntent);
+        } else if (containsButton(mVerifyAppLinkCardItem, v)) {
+            setPassState(mVerifyAppLinkCardItem, true);
+            getPassButton().setEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        if (mSelectAppLinkItemPassed
+                && TextUtils.equals(MockTvInputSetupActivity.APP_LINK_TEST_VALUE,
+                        intent.getStringExtra(MockTvInputSetupActivity.APP_LINK_TEST_KEY))) {
+            getPostTarget().removeCallbacks(mSelectAppLinkFailCallback);
+            setPassState(mVerifyAppLinkIntentItem, true);
+            setButtonEnabled(mVerifyAppLinkCardItem, true);
+        }
+    }
+
+    @Override
+    protected void createTestItems() {
+        mSelectAppLinkItem = createUserItem(R.string.tv_app_link_test_select_app_link,
+                R.string.tv_launch_tv_app, this);
+        setButtonEnabled(mSelectAppLinkItem, true);
+        mVerifyAppLinkIntentItem = createAutoItem(
+                R.string.tv_app_link_test_verify_link_clicked);
+        mVerifyAppLinkCardItem = createUserItem(R.string.tv_input_link_test_verify_link_interface,
+                android.R.string.yes, this);
+        TextView instructions = (TextView) mVerifyAppLinkCardItem.findViewById(R.id.instructions);
+        Drawable image = getDrawable(R.drawable.app_link_img);
+        image.setBounds(0, 0, 317, 241);
+        instructions.setCompoundDrawablePadding(10);
+        instructions.setCompoundDrawables(image, null, null, null);
+    }
+
+    @Override
+    protected void setInfoResources() {
+        setInfoResources(R.string.tv_app_link_test, R.string.tv_app_link_test_info, -1);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
index c05b753..43ed7da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -17,9 +17,12 @@
 package com.android.cts.verifier.tv;
 
 import android.app.Activity;
+import android.content.ComponentName;
 import android.content.ContentUris;
 import android.content.ContentValues;
+import android.content.Intent;
 import android.database.Cursor;
+import android.graphics.Color;
 import android.media.tv.TvContract;
 import android.media.tv.TvContract.Programs;
 import android.media.tv.TvInputInfo;
@@ -28,6 +31,8 @@
 import android.util.Pair;
 import android.view.View;
 
+import com.android.cts.verifier.R;
+
 import java.util.ArrayList;
 
 public class MockTvInputSetupActivity extends Activity {
@@ -38,6 +43,10 @@
 
     /* package-private */ static final String PROGRAM_TITLE = "Dummy Program";
     /* package-private */ static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
+
+    /* package-private */ static final String APP_LINK_TEST_KEY = "app_link_test_key";
+    /* package-private */ static final String APP_LINK_TEST_VALUE = "app_link_test_value";
+    private static final String APP_LINK_TEXT = "Cts App-Link Text";
     private static final long PROGRAM_LENGTH_MILLIS = 60 * 60 * 1000;
     private static final int PROGRAM_COUNT = 24;
 
@@ -69,6 +78,18 @@
             values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
             values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER);
             values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, CHANNEL_NAME);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_TEXT, APP_LINK_TEXT);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_COLOR, Color.RED);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_ICON_URI,
+                    "android.resource://" + getPackageName() + "/" + R.drawable.icon);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+                    "android.resource://" + getPackageName() + "/" + R.raw.sns_texture);
+            Intent appLinkIntentUri = new Intent(this, AppLinkTestActivity.class);
+            appLinkIntentUri.putExtra(APP_LINK_TEST_KEY, APP_LINK_TEST_VALUE);
+            appLinkIntentUri.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+            values.put(TvContract.Channels.COLUMN_APP_LINK_INTENT_URI,
+                    appLinkIntentUri.toUri(Intent.URI_INTENT_SCHEME));
+
             Uri channelUri = getContentResolver().insert(uri, values);
             // If the channel's ID happens to be zero, we add another and delete the one.
             if (ContentUris.parseId(channelUri) == 0) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index 12e9652..acab18e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -35,8 +35,6 @@
 public abstract class TvAppVerifierActivity extends PassFailButtons.Activity {
     private static final String TAG = "TvAppVerifierActivity";
 
-    private static final long TIMEOUT_MS = 5l * 60l * 1000l;  // 5 mins.
-
     private LayoutInflater mInflater;
     private ViewGroup mItemList;
     private View mPostTarget;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
index 06f4f6f..e8e2cee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -25,8 +25,6 @@
 
 import com.android.cts.verifier.R;
 
-import java.util.List;
-
 /**
  * Tests for verifying TV app behavior for third-party TV input apps.
  */
@@ -131,7 +129,7 @@
         mGoToEpgItem = createUserItem(R.string.tv_input_discover_test_go_to_epg,
                 R.string.tv_launch_epg, this);
         mVerifyEpgItem = createUserItem(R.string.tv_input_discover_test_verify_epg,
-                R.string.tv_input_discover_test_yes, this);
+                android.R.string.yes, this);
     }
 
     @Override
diff --git a/apps/cts-usb-accessory/Android.mk b/apps/cts-usb-accessory/Android.mk
index 8d18da3..f3a7ebd 100644
--- a/apps/cts-usb-accessory/Android.mk
+++ b/apps/cts-usb-accessory/Android.mk
@@ -33,6 +33,7 @@
 LOCAL_STATIC_LIBRARIES := libusbhost libcutils
 LOCAL_LDLIBS += -lpthread
 LOCAL_CFLAGS := -g -O0
+LOCAL_CXX_STL := none
 
 include $(BUILD_HOST_EXECUTABLE)
 
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index 600f6a1..0aaa8aa 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -19,7 +19,7 @@
 DEVICE_INFO_PACKAGE := com.android.compatibility.common.deviceinfo
 DEVICE_INFO_INSTRUMENT := com.android.compatibility.common.deviceinfo.DeviceInfoInstrument
 DEVICE_INFO_PERMISSIONS += android.permission.WRITE_EXTERNAL_STORAGE
-DEVICE_INFO_ACTIVITIES += $(DEVICE_INFO_PACKAGE).GenericDeviceInfo
+DEVICE_INFO_ACTIVITIES += $(DEVICE_INFO_PACKAGE).GenericDeviceInfo $(DEVICE_INFO_PACKAGE).PackageDeviceInfo
 
 # Add the base device info
 LOCAL_STATIC_JAVA_LIBRARIES += compatibility-device-info
diff --git a/build/test_deqp_package.mk b/build/test_deqp_package.mk
index 8fbae26..650875c 100644
--- a/build/test_deqp_package.mk
+++ b/build/test_deqp_package.mk
@@ -18,15 +18,14 @@
 
 CTS_DEQP_CONFIG_PATH := $(call my-dir)
 
-cts_library_xml := $(CTS_TESTCASES_OUT)/com.drawelements.deqp.$(DEQP_API).xml
-$(cts_library_xml): MUSTPASS_XML_FILE := external/deqp/android/cts/master/com.drawelements.deqp.$(DEQP_API).xml
-$(cts_library_xml): PRIVATE_TEST_NAME := $(DEQP_TEST_NAME)
-$(cts_library_xml): PRIVATE_TEST_PACKAGE := com.drawelements.deqp.$(DEQP_API)
-$(cts_library_xml): PRIVATE_DUMMY_CASELIST := $(CTS_DEQP_CONFIG_PATH)/deqp_dummy_test_list
-$(cts_library_xml): external/deqp/android/cts/master/com.drawelements.deqp.$(DEQP_API).xml $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_XML_GENERATOR)
-	$(hide) echo Generating test description for $(PRIVATE_TEST_NAME)
+cts_library_xmls:=$(foreach xml_file, $(call find-files-in-subdirs, external/deqp/android/cts/master, 'com.drawelements.deqp.$(DEQP_API).*xml', .), $(CTS_TESTCASES_OUT)/$(xml_file))
+$(cts_library_xmls) : PRIVATE_API := $(DEQP_API)
+$(cts_library_xmls) : PRIVATE_TEST_NAME := $(DEQP_TEST_NAME)
+$(cts_library_xmls) : PRIVATE_TEST_PACKAGE := com.drawelements.deqp.$(DEQP_API)
+$(cts_library_xmls) : PRIVATE_DUMMY_CASELIST := $(CTS_DEQP_CONFIG_PATH)/deqp_dummy_test_list
+$(cts_library_xmls) : $(CTS_TESTCASES_OUT)/%.xml: external/deqp/android/cts/master/%.xml $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_XML_GENERATOR)
+	$(hide) echo Generating test description $@
 	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
-
 # Query build ABIs by routing a dummy test list through xml generator and parse result
 	$(hide) $(eval supported_abi_attr := $(shell $(CTS_XML_GENERATOR) -t dummyTest \
 										-n dummyName \
@@ -36,8 +35,7 @@
 										-a $(CTS_TARGET_ARCH) \
 										< $(PRIVATE_DUMMY_CASELIST) \
 										| grep --only-matching -e " abis=\"[^\"]*\""))
-
 # Patch xml caselist with supported abi
 	$(hide) $(SED_EXTENDED) -e 's:^(\s*)<Test ((.[^/]|[^/])*)(/?)>$$:\1<Test \2 $(supported_abi_attr)\4>:' \
-				< $(MUSTPASS_XML_FILE) \
+				< $< \
 				> $@
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java
index 62e512c..f9de6eb 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java
@@ -182,6 +182,17 @@
     /**
      * Start a new group of result.
      */
+    public void startGroup() {
+        try {
+            mJsonWriter.beginObject();
+        } catch (Exception e) {
+            error("Failed to begin JSON group: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Start a new group of result with specified name.
+     */
     public void startGroup(String name) {
         try {
             mJsonWriter.name(name);
@@ -203,6 +214,41 @@
     }
 
     /**
+     * Start a new array of result.
+     */
+    public void startArray() {
+        try {
+            mJsonWriter.beginArray();
+        } catch (Exception e) {
+            error("Failed to begin JSON array: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Start a new array of result with specified name.
+     */
+    public void startArray(String name) {
+        checkName(name);
+        try {
+            mJsonWriter.name(name);
+            mJsonWriter.beginArray();
+        } catch (Exception e) {
+            error("Failed to begin JSON array: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Complete adding result to the last started array.
+     */
+    public void endArray() {
+        try {
+            mJsonWriter.endArray();
+        } catch (Exception e) {
+            error("Failed to end JSON group: " + e.getMessage());
+        }
+    }
+
+    /**
      * Add a double value result.
      */
     public void addResult(String name, double value) {
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java
index f3af0bc..2f80911 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java
@@ -25,19 +25,37 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * An instrumentation that runs all activities that extends DeviceInfoActivity.
  */
 public class DeviceInfoInstrument extends Instrumentation {
 
     private static final String LOG_TAG = "ExtendedDeviceInfo";
-    private static final int DEVICE_INFO_ACTIVITY_REQUEST = 1;
+    private static final String COLLECTOR = "collector";
 
+    // List of collectors to run. If null or empty, all collectors will run.
+    private Set<String> mCollectorSet = new HashSet<String>();
+
+    // Results sent to the caller when this istrumentation completes.
     private Bundle mBundle = new Bundle();
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        if (savedInstanceState != null) {
+            String collectorList = savedInstanceState.getString(COLLECTOR);
+            if (!TextUtils.isEmpty(collectorList)) {
+                for (String collector : TextUtils.split(collectorList, ",")) {
+                  if (!TextUtils.isEmpty(collector)) {
+                    mCollectorSet.add(collector);
+                  }
+                }
+            }
+        }
         start();
     }
 
@@ -47,13 +65,8 @@
             Context context = getContext();
             ActivityInfo[] activities = context.getPackageManager().getPackageInfo(
                     context.getPackageName(), PackageManager.GET_ACTIVITIES).activities;
-
             for (ActivityInfo activityInfo : activities) {
-                Class cls = Class.forName(activityInfo.name);
-                if (cls != DeviceInfoActivity.class &&
-                        DeviceInfoActivity.class.isAssignableFrom(cls)) {
-                    runActivity(activityInfo.name);
-                }
+                runActivity(activityInfo.name);
             }
         } catch (Exception e) {
             Log.e(LOG_TAG, "Exception occurred while running activities.", e);
@@ -65,9 +78,40 @@
     }
 
     /**
+     * Returns true if the activity meets the criteria to run; otherwise, false.
+     */
+    private boolean isActivityRunnable(Class activityClass) {
+        // Don't run the base DeviceInfoActivity class.
+        if (DeviceInfoActivity.class == activityClass) {
+            return false;
+        }
+        // Don't run anything that doesn't extends DeviceInfoActivity.
+        if (!DeviceInfoActivity.class.isAssignableFrom(activityClass)) {
+            return false;
+        }
+        // Only run activity if mCollectorSet is empty or contains it.
+        if (mCollectorSet != null && mCollectorSet.size() > 0) {
+            return mCollectorSet.contains(activityClass.getName());
+        }
+        // Run anything that makes it here.
+        return true;
+    }
+
+    /**
      * Runs a device info activity and return the file path where the results are written to.
      */
     private void runActivity(String activityName) throws Exception {
+        Class activityClass = null;
+        try {
+            activityClass = Class.forName(activityName);
+        } catch (ClassNotFoundException e) {
+            return;
+        }
+
+        if (activityClass == null || !isActivityRunnable(activityClass)) {
+            return;
+        }
+
         Intent intent = new Intent();
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setClassName(this.getContext(), activityName);
@@ -76,7 +120,7 @@
         waitForIdleSync();
         activity.waitForActivityToFinish();
 
-        String className = Class.forName(activityName).getSimpleName();
+        String className = activityClass.getSimpleName();
         String errorMessage = activity.getErrorMessage();
         if (TextUtils.isEmpty(errorMessage)) {
             mBundle.putString(className, activity.getResultFilePath());
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
index f6e7c57..decf6dd 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
@@ -68,6 +68,8 @@
     public static final String BUILD_SERIAL = "build_serial";
     public static final String BUILD_VERSION_RELEASE = "build_version_release";
     public static final String BUILD_VERSION_SDK = "build_version_sdk";
+    public static final String BUILD_VERSION_BASE_OS = "build_version_base_os";
+    public static final String BUILD_VERSION_SECURITY_PATH = "build_version_security_patch";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -93,5 +95,7 @@
         addResult(BUILD_SERIAL, Build.SERIAL);
         addResult(BUILD_VERSION_RELEASE, Build.VERSION.RELEASE);
         addResult(BUILD_VERSION_SDK, Build.VERSION.SDK);
+        addResult(BUILD_VERSION_BASE_OS, Build.VERSION.BASE_OS);
+        addResult(BUILD_VERSION_SECURITY_PATH, Build.VERSION.SECURITY_PATCH);
     }
-}
\ No newline at end of file
+}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
new file mode 100644
index 0000000..4d9ad46
--- /dev/null
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -0,0 +1,50 @@
+/*
+ * 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.compatibility.common.deviceinfo;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import com.android.compatibility.common.deviceinfo.DeviceInfoActivity;
+
+/**
+ * PackageDeviceInfo collector.
+ */
+public class PackageDeviceInfo extends DeviceInfoActivity {
+
+    private static final String PACKAGE = "package";
+    private static final String NAME = "name";
+    private static final String VERSION_NAME = "version_name";
+    private static final String SYSTEM_PRIV = "system_priv";
+    private static final String PRIV_APP_DIR = "/system/priv-app";
+
+    @Override
+    protected void collectDeviceInfo() {
+        PackageManager pm = this.getPackageManager();
+        startArray(PACKAGE);
+        for (PackageInfo pkg : pm.getInstalledPackages(0)) {
+            startGroup();
+            addResult(NAME, pkg.packageName);
+            addResult(VERSION_NAME, pkg.versionName);
+
+            String dir = pkg.applicationInfo.sourceDir;
+            addResult(SYSTEM_PRIV, dir != null && dir.startsWith(PRIV_APP_DIR));
+            endGroup();
+        }
+        endArray(); // Package
+    }
+}
diff --git a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java
index 8a8a66c..d092f4e 100644
--- a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java
+++ b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java
@@ -25,15 +25,15 @@
 /**
  * Test for {@link DeviceInfoActivity}.
  */
-public class DeviceInfoActivityTest extends ActivityInstrumentationTestCase2<SampleDeviceInfo> {
+public class DeviceInfoActivityTest extends ActivityInstrumentationTestCase2<TestDeviceInfo> {
 
     private static final String EXPECTED_FILE_PATH =
-            "/storage/emulated/0/device-info-files/SampleDeviceInfo.deviceinfo.json";
+            "/storage/emulated/0/device-info-files/TestDeviceInfo.deviceinfo.json";
 
-    private SampleDeviceInfo mActivity;
+    private TestDeviceInfo mActivity;
 
     public DeviceInfoActivityTest() {
-        super(SampleDeviceInfo.class);
+        super(TestDeviceInfo.class);
     }
 
     @Override
@@ -59,7 +59,7 @@
         assertEquals("Incorrect file path", EXPECTED_FILE_PATH, resultFilePath);
         // Check json file content
         String jsonContent = readFile(resultFilePath);
-        assertEquals("Incorrect json output", ExampleObjects.sampleDeviceInfoJson(), jsonContent);
+        assertEquals("Incorrect json output", ExampleObjects.testDeviceInfoJson(), jsonContent);
     }
 
     private String readFile(String filePath) throws IOException {
diff --git a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java
index 92e7df1..570bdd5 100644
--- a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java
+++ b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java
@@ -20,53 +20,73 @@
  */
 public final class ExampleObjects {
 
+    // Must match DeviceInfoActivity.MAX_STRING_VALUE_LENGTH and
+    // DeviceInfoActivity.MAX_ARRAY_LENGTH
     private static final int MAX_LENGTH = 1000;
 
-    private static final String SAMPLE_DEVICE_INFO_JSON = "{\n" +
-        "  \"foo\": {\n" +
-        "    \"foo_boolean\": true,\n" +
-        "    \"bar\": {\n" +
-        "      \"bar_string\": [\n" +
-        "        \"bar-string-1\",\n" +
-        "        \"bar-string-2\",\n" +
-        "        \"bar-string-3\"\n" +
-        "      ],\n" +
-        "      \"bar_boolean\": [\n" +
-        "        true,\n" +
-        "        false\n" +
-        "      ],\n" +
-        "      \"bar_double\": [\n" +
-        "        1.7976931348623157E308,\n" +
-        "        4.9E-324\n" +
-        "      ],\n" +
-        "      \"bar_int\": [\n" +
-        "        2147483647,\n" +
-        "        -2147483648\n" +
-        "      ],\n" +
-        "      \"bar_long\": [\n" +
-        "        9223372036854775807,\n" +
-        "        -9223372036854775808\n" +
+    private static final String TEST_DEVICE_INFO_JSON = "{\n" +
+        "  \"test_boolean\": true,\n" +
+        "  \"test_double\": 1.23456789,\n" +
+        "  \"test_int\": 123456789,\n" +
+        "  \"test_long\": 9223372036854775807,\n" +
+        "  \"test_string\": \"test string\",\n" +
+        "  \"test_strings\": [\n" +
+        "    \"test string 1\",\n" +
+        "    \"test string 2\",\n" +
+        "    \"test string 3\"\n" +
+        "  ],\n" +
+        "  \"test_group\": {\n" +
+        "    \"test_boolean\": false,\n" +
+        "    \"test_double\": 9.87654321,\n" +
+        "    \"test_int\": 987654321,\n" +
+        "    \"test_long\": 9223372036854775807,\n" +
+        "    \"test_string\": \"test group string\",\n" +
+        "    \"test_strings\": [\n" +
+        "      \"test group string 1\",\n" +
+        "      \"test group string 2\",\n" +
+        "      \"test group string 3\"\n" +
+        "    ]\n" +
+        "  },\n" +
+        "  \"test_groups\": [\n" +
+        "    {\n" +
+        "      \"test_string\": \"test groups string 1\",\n" +
+        "      \"test_strings\": [\n" +
+        "        \"test groups string 1-1\",\n" +
+        "        \"test groups string 1-2\",\n" +
+        "        \"test groups string 1-3\"\n" +
         "      ]\n" +
         "    },\n" +
-        "    \"foo_double\": 1.7976931348623157E308,\n" +
-        "    \"foo_int\": 2147483647,\n" +
-        "    \"foo_long\": 9223372036854775807,\n" +
-        "    \"foo_string\": \"foo-string\",\n" +
-        "    \"long_string\": \"%s\",\n" +
-        "    \"long_int_array\": [\n%s" +
-        "    ]\n" +
-        "  }\n" +
+        "    {\n" +
+        "      \"test_string\": \"test groups string 2\",\n" +
+        "      \"test_strings\": [\n" +
+        "        \"test groups string 2-1\",\n" +
+        "        \"test groups string 2-2\",\n" +
+        "        \"test groups string 2-3\"\n" +
+        "      ]\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"test_string\": \"test groups string 3\",\n" +
+        "      \"test_strings\": [\n" +
+        "        \"test groups string 3-1\",\n" +
+        "        \"test groups string 3-2\",\n" +
+        "        \"test groups string 3-3\"\n" +
+        "      ]\n" +
+        "    }\n" +
+        "  ],\n" +
+        "  \"max_length_string\": \"%s\",\n" +
+        "  \"max_num_ints\": [\n%s" +
+        "  ]\n" +
         "}\n";
 
-    public static String sampleDeviceInfoJson() {
+    public static String testDeviceInfoJson() {
         StringBuilder longStringSb = new StringBuilder();
         StringBuilder longArraySb = new StringBuilder();
         int lastNum = MAX_LENGTH - 1;
         for (int i = 0; i < MAX_LENGTH; i++) {
             longStringSb.append("a");
-            longArraySb.append(String.format("      %d%s\n", i, ((i == lastNum)? "" : ",")));
+            longArraySb.append(String.format("    %d%s\n", i, ((i == lastNum)? "" : ",")));
         }
-        return String.format(SAMPLE_DEVICE_INFO_JSON,
+        return String.format(TEST_DEVICE_INFO_JSON,
             longStringSb.toString(), longArraySb.toString());
     }
 }
\ No newline at end of file
diff --git a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/TestDeviceInfo.java b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/TestDeviceInfo.java
new file mode 100644
index 0000000..7f82942
--- /dev/null
+++ b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/TestDeviceInfo.java
@@ -0,0 +1,85 @@
+/*
+ * 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.compatibility.common.deviceinfo;
+
+import android.os.Bundle;
+
+import java.lang.StringBuilder;
+
+/**
+ * Collector for testing DeviceInfoActivity
+ */
+public class TestDeviceInfo extends DeviceInfoActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void collectDeviceInfo() {
+
+        // Test primitive results
+        addResult("test_boolean", true);
+        addResult("test_double", 1.23456789);
+        addResult("test_int", 123456789);
+        addResult("test_long", Long.MAX_VALUE);
+        addResult("test_string", "test string");
+        addArray("test_strings", new String[] {
+            "test string 1",
+            "test string 2",
+            "test string 3",
+        });
+
+        // Test group
+        startGroup("test_group");
+        addResult("test_boolean", false);
+        addResult("test_double", 9.87654321);
+        addResult("test_int", 987654321);
+        addResult("test_long", Long.MAX_VALUE);
+        addResult("test_string", "test group string");
+        addArray("test_strings", new String[] {
+            "test group string 1",
+            "test group string 2",
+            "test group string 3"
+        });
+        endGroup(); // test_group
+
+        // Test array of groups
+        startArray("test_groups");
+        for (int i = 1; i < 4; i++) {
+            startGroup();
+            addResult("test_string", "test groups string " + i);
+            addArray("test_strings", new String[] {
+                "test groups string " + i + "-1",
+                "test groups string " + i + "-2",
+                "test groups string " + i + "-3"
+            });
+            endGroup();
+        }
+        endArray(); // test_groups
+
+        // Test max
+        StringBuilder sb = new StringBuilder();
+        int[] arr = new int[1001];
+        for (int i = 0; i < 1001; i++) {
+            sb.append("a");
+            arr[i] = i;
+        }
+        addResult("max_length_string", sb.toString());
+        addArray("max_num_ints", arr);
+    }
+}
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
index 70414ca..2ae2e10 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
@@ -294,7 +294,7 @@
     private LocalVolumeInfo getAdoptionVolume() throws Exception {
         String[] lines = null;
         int attempt = 0;
-        while (attempt++ < 5) {
+        while (attempt++ < 15) {
             lines = getDevice().executeShellCommand("sm list-volumes private").split("\n");
             for (String line : lines) {
                 final LocalVolumeInfo info = new LocalVolumeInfo(line.trim());
diff --git a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
index a86f7f0..c460300 100644
--- a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
+++ b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
@@ -19,7 +19,7 @@
     A simple app with a tracing section to test that apps tracing signals are
     emitted by atrace.
     -->
-    <application>
+    <application android:debuggable="true"> <!-- Debuggable to enable tracing -->
         <activity android:name=".AtraceTestAppActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index 78da8ac..760723e 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -208,6 +208,7 @@
                     installResult);
 
             // capture a launch of the app with async tracing
+            // content traced by 'view' tag tested below, 'sched' used to ensure tgid printed
             String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
             getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
             getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
@@ -227,13 +228,15 @@
         String traceData = atraceOutput.substring(dataStart + MARKER.length());
 
         FtraceEntryCallback callback = new FtraceEntryCallback() {
-            private int matches = 0;
-            private int nextSectionIndex = 0;
-            private int appPid = -1;
+            private int userSpaceMatches = 0;
+            private int beginMatches = 0;
+            private int nextSectionIndex = -1;
+            private int appTid = -1;
 
-            // list of tags expected to be seen on app launch, in order.
+
+            private final String initialSection = "traceable-app-test-section";
+            // list of tags expected to be seen on app launch, in order, after the initial.
             private final String[] requiredSectionList = {
-                    "traceable-app-test-section",
                     "inflate",
                     "Choreographer#doFrame",
                     "traversal",
@@ -251,34 +254,41 @@
                     return;
                 }
 
-                matches++;
                 assertNotNull(truncatedThreadName);
                 assertTrue(tid > 0);
-                if (TEST_PKG.endsWith(truncatedThreadName)) {
-                    matches++;
+                userSpaceMatches++;
 
-                    if (pid >= 0) {
-                        // verify pid, if present
-                        if (appPid == -1) {
-                            appPid = pid;
-                        } else {
-                            assertEquals(appPid, pid);
-                        }
-                    }
+                if (details == null || !details.startsWith("B|")) {
+                    // not a begin event
+                    return;
+                }
+                beginMatches++;
 
-                    if (nextSectionIndex < requiredSectionList.length
-                            && details != null
-                            && details.startsWith("B|")
-                            && details.endsWith("|" + requiredSectionList[nextSectionIndex])) {
-                        nextSectionIndex++;
-                    }
+                if (details.endsWith("|" + initialSection)) {
+                    // initial section observed, start looking for others in order
+                    assertEquals(nextSectionIndex, -1);
+                    nextSectionIndex = 0;
+                    appTid = tid;
+                    return;
+                }
+
+                if (nextSectionIndex >= 0
+                        && tid == appTid
+                        && nextSectionIndex < requiredSectionList.length
+                        && details.endsWith("|" + requiredSectionList[nextSectionIndex])) {
+                    // found next required section in sequence
+                    nextSectionIndex++;
                 }
             }
 
             @Override
             public void onFinished() {
                 assertTrue("Unable to parse any userspace sections from atrace output",
-                        matches != 0);
+                        userSpaceMatches != 0);
+                assertTrue("Unable to parse any section begin events from atrace output",
+                        beginMatches != 0);
+                assertTrue("Unable to parse initial userspace sections from test app",
+                        nextSectionIndex >= 0);
                 assertEquals("Didn't see required list of traced sections, in order",
                         requiredSectionList.length, nextSectionIndex);
             }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
index ed920e9..fe07bcb 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
@@ -33,6 +33,15 @@
                 <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
             </intent-filter>
         </receiver>
+        <receiver
+            android:name="com.android.cts.deviceandprofileowner.PrimaryUserDeviceAdmin"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/primary_device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
         <activity
             android:name="com.android.cts.deviceandprofileowner.ApplicationRestrictionsActivity" />
         <activity
@@ -62,6 +71,13 @@
                        android:resource="@xml/authenticator" />
         </service>
 
+        <activity android:name=".UserRestrictionActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml
new file mode 100644
index 0000000..a6aff49
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+    <uses-policies>
+         <reset-password />
+         <limit-password />
+    </uses-policies>
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
similarity index 96%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
index de859b4..3eb8c35 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.cts.managedprofile;
+package com.android.cts.deviceandprofileowner;
 
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
@@ -40,14 +40,12 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
-
 /**
  * Exercise delegated cert installer APIs in {@link DevicePolicyManager} by setting the test app
  * (CtsCertInstallerApp) as a delegated cert installer and then asking it to invoke various
  * cert-related APIs. The expected certificate changes are validated both remotely and locally.
  */
-public class DelegatedCertInstallerTest extends AndroidTestCase {
+public class DelegatedCertInstallerTest extends BaseDeviceAdminTest {
 
     private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller";
 
@@ -163,12 +161,12 @@
         mReceivedException = null;
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_CERT_OPERATION_DONE);
-        getContext().registerReceiver(receiver, filter);
+        mContext.registerReceiver(receiver, filter);
     }
 
     @Override
     public void tearDown() throws Exception {
-        getContext().unregisterReceiver(receiver);
+        mContext.unregisterReceiver(receiver);
         mDpm.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes());
         // Installed private key pair will be removed once the lockscreen password is cleared,
         // which is done in the hostside test.
@@ -246,7 +244,7 @@
         intent.setAction(ACTION_INSTALL_CERT);
         intent.putExtra(EXTRA_CERT_DATA, cert);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(intent);
+        mContext.startActivity(intent);
     }
 
     private void removeCaCert(byte[] cert) {
@@ -254,7 +252,7 @@
         intent.setAction(ACTION_REMOVE_CERT);
         intent.putExtra(EXTRA_CERT_DATA, cert);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(intent);
+        mContext.startActivity(intent);
     }
 
     private void verifyCaCert(byte[] cert) {
@@ -262,7 +260,7 @@
         intent.setAction(ACTION_VERIFY_CERT);
         intent.putExtra(EXTRA_CERT_DATA, cert);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(intent);
+        mContext.startActivity(intent);
     }
 
     private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException {
@@ -288,6 +286,6 @@
         intent.putExtra(EXTRA_KEY_DATA, key);
         intent.putExtra(EXTRA_KEY_ALIAS, alias);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(intent);
+        mContext.startActivity(intent);
     }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserAdminHelper.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserAdminHelper.java
new file mode 100644
index 0000000..7fc0173
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserAdminHelper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+/**
+ * This test executes helper tasks as active device admin in the primary user. Current tasks are
+ * setting and clearing lockscreen password used by the host side delegated cert installer test.
+ */
+public class PrimaryUserAdminHelper extends AndroidTestCase {
+
+    private DevicePolicyManager mDpm;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+    }
+
+    /**
+     * Device admin can only be deactivated by itself and this test should be executed before the
+     * device admin package can be uninstalled.
+     */
+    public void testClearDeviceAdmin() throws Exception {
+        ComponentName cn = PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT;
+        if (mDpm.isAdminActive(cn)) {
+            mDpm.removeActiveAdmin(cn);
+            // Wait until device admin is not active (with 2 minutes timeout).
+            for (int i = 0; i < 2 * 60 && mDpm.isAdminActive(cn); i++) {
+                Thread.sleep(1000);  // 1 second.
+            }
+        }
+        assertFalse(mDpm.isAdminActive(cn));
+    }
+
+    /**
+     * Set lockscreen password.
+     */
+    public void testSetPassword() {
+        // Enable credential storage by setting a nonempty password.
+        assertTrue(mDpm.resetPassword("test", 0));
+    }
+
+    /**
+     * Clear lockscreen password.
+     */
+    public void testClearPassword() {
+        mDpm.setPasswordQuality(PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT,
+                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+        mDpm.setPasswordMinimumLength(
+                PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT, 0);
+        assertTrue(mDpm.resetPassword("", 0));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserDeviceAdmin.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserDeviceAdmin.java
new file mode 100644
index 0000000..f3c8ff6
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserDeviceAdmin.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.ComponentName;
+
+/**
+ * A device admin class running in the primary user. Currently used by delegated cert installer
+ * test to set a lockscreen password which is prerequisite of installKeyPair().
+ */
+public class PrimaryUserDeviceAdmin extends DeviceAdminReceiver {
+    public static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
+            PrimaryUserDeviceAdmin.class.getPackage().getName(),
+            PrimaryUserDeviceAdmin.class.getName());
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionActivity.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionActivity.java
new file mode 100644
index 0000000..fed1a79
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionActivity.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.Log;
+
+/**
+ * Simple activity that adds or clears a user restriction depending on the value of the extras.
+ */
+public class UserRestrictionActivity extends Activity {
+
+    private static final String TAG = UserRestrictionActivity.class.getName();
+
+    private static final String EXTRA_RESTRICTION_KEY = "extra-restriction-key";
+    private static final String EXTRA_COMMAND = "extra-command";
+
+    private static final String ADD_COMMAND = "add-restriction";
+    private static final String CLEAR_COMMAND = "clear-restriction";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        handleIntent(getIntent());
+    }
+
+    // Overriding this method in case another intent is sent to this activity before finish()
+    @Override
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        handleIntent(intent);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        // Calling finish() here because doing it in onCreate(), onStart() or onResume() makes
+        // "adb shell am start" timeout if using the -W option.
+        finish();
+    }
+
+    private void handleIntent(Intent intent) {
+        DevicePolicyManager dpm = (DevicePolicyManager)
+                getSystemService(Context.DEVICE_POLICY_SERVICE);
+        String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
+        String command = intent.getStringExtra(EXTRA_COMMAND);
+        Log.i(TAG, "Command: \"" + command + "\". Restriction: \"" + restrictionKey + "\"");
+
+        if (ADD_COMMAND.equals(command)) {
+            dpm.addUserRestriction(BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT, restrictionKey);
+            Log.i(TAG, "Added user restriction " + restrictionKey
+                    + " for user " + Process.myUserHandle());
+        } else if (CLEAR_COMMAND.equals(command)) {
+            dpm.clearUserRestriction(
+                    BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT, restrictionKey);
+            Log.i(TAG, "Cleared user restriction " + restrictionKey
+                    + " for user " + Process.myUserHandle());
+        } else {
+            Log.e(TAG, "Invalid command: " + command);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
index 5cad040..f305e86 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
@@ -61,7 +61,8 @@
             // Otherwise, we'll wait until we receive it.
             return;
         }
-        assertTrue(mPreferenceChanged.tryAcquire(40, TimeUnit.SECONDS));
+        // We're relying on background broadcast intents, which can take a long time.
+        assertTrue(mPreferenceChanged.tryAcquire(2, TimeUnit.MINUTES));
         assertTrue(mPreferences.getBoolean(
                 BroadcastIntentReceiver.OWNER_CHANGED_BROADCAST_RECEIVED_KEY, false));
     }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
index 0ec3d1e..4bac8a5 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
@@ -14,8 +14,6 @@
 -->
 <device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
     <uses-policies>
-         <reset-password />
-         <limit-password />
          <disable-camera />
     </uses-policies>
 </device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
index af0ac25..47f8716 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
@@ -21,8 +21,7 @@
 import android.test.AndroidTestCase;
 
 /**
- * This test executes helper tasks as active device admin in the primary user. Current tasks are
- * setting and clearing lockscreen password used by the host side delegated cert installer test.
+ * This test executes helper tasks as active device admin in the primary user.
  */
 public class PrimaryUserAdminHelper extends AndroidTestCase {
 
@@ -49,23 +48,4 @@
         }
         assertFalse(mDpm.isAdminActive(cn));
     }
-
-    /**
-     * Set lockscreen password.
-     */
-    public void testSetPassword() {
-        // Enable credential storage by setting a nonempty password.
-        assertTrue(mDpm.resetPassword("test", 0));
-    }
-
-    /**
-     * Clear lockscreen password.
-     */
-    public void testClearPassword() {
-        mDpm.setPasswordQuality(PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT,
-                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
-        mDpm.setPasswordMinimumLength(
-                PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT, 0);
-        assertTrue(mDpm.resetPassword("", 0));
-    }
 }
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
new file mode 100644
index 0000000..e68c884
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsPackageInstallerApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/AndroidManifest.xml b/hostsidetests/devicepolicy/app/PackageInstaller/AndroidManifest.xml
new file mode 100644
index 0000000..aaa88f2
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.packageinstaller">
+
+    <uses-sdk android:minSdkVersion="21"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <receiver
+            android:name=".ClearDeviceOwnerTest$BasicAdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.packageinstaller"
+        android:label="Package Installer CTS Tests" />
+
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/PackageInstaller/res/xml/device_admin.xml
new file mode 100644
index 0000000..de4a9e1
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/res/xml/device_admin.xml
@@ -0,0 +1,3 @@
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+    <uses-policies />
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
similarity index 70%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
rename to hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
index 0eddbee..f994e9e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
@@ -13,17 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.cts.deviceowner;
+package com.android.cts.packageinstaller;
 
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.support.test.uiautomator.UiDevice;
+import android.test.InstrumentationTestCase;
+
+import com.android.cts.packageinstaller.ClearDeviceOwnerTest.BasicAdminReceiver;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -31,34 +37,41 @@
 import java.io.OutputStream;
 
 /**
- * This class tests silent package install and uninstall by a device owner.
+ * Base test case for testing PackageInstaller.
  */
-public class PackageInstallTest extends BaseDeviceOwnerTest {
-    private static final String TEST_APP_LOCATION = "/data/local/tmp/CtsSimpleApp.apk";
-    private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
-    private static final int PACKAGE_INSTALLER_TIMEOUT_MS = 60000; // 60 seconds
+public class BasePackageInstallTest extends InstrumentationTestCase {
+    protected static final String TEST_APP_LOCATION = "/data/local/tmp/CtsSimpleApp.apk";
+    protected static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+    protected static final int PACKAGE_INSTALLER_TIMEOUT_MS = 60000; // 60 seconds
     private static final String ACTION_INSTALL_COMMIT =
             "com.android.cts.deviceowner.INTENT_PACKAGE_INSTALL_COMMIT";
-    private static final int PACKAGE_INSTALLER_STATUS_UNDEFINED = -1000;
+    protected static final int PACKAGE_INSTALLER_STATUS_UNDEFINED = -1000;
+    public static final String PACKAGE_NAME = SilentPackageInstallTest.class.getPackage().getName();
 
+    protected Context mContext;
+    protected UiDevice mDevice;
+    protected DevicePolicyManager mDevicePolicyManager;
     private PackageManager mPackageManager;
     private PackageInstaller mPackageInstaller;
     private PackageInstaller.Session mSession;
-    private boolean mCallbackReceived;
-    private int mCallbackStatus;
+    protected boolean mCallbackReceived;
+    protected int mCallbackStatus;
+    protected Intent mCallbackIntent;
 
-    private final Object mPackageInstallerTimeoutLock = new Object();
+    protected final Object mPackageInstallerTimeoutLock = new Object();
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            mContext.unregisterReceiver(this);
             synchronized (mPackageInstallerTimeoutLock) {
                 mCallbackStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
                         PACKAGE_INSTALLER_STATUS_UNDEFINED);
                 if (mCallbackStatus == PackageInstaller.STATUS_SUCCESS) {
+                    mContext.unregisterReceiver(this);
                     assertEquals(TEST_APP_PKG, intent.getStringExtra(
                             PackageInstaller.EXTRA_PACKAGE_NAME));
+                } else if (mCallbackStatus == PackageInstaller.STATUS_PENDING_USER_ACTION) {
+                    mCallbackIntent = (Intent) intent.getExtras().get(Intent.EXTRA_INTENT);
                 }
                 mCallbackReceived = true;
                 mPackageInstallerTimeoutLock.notify();
@@ -69,6 +82,10 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mContext = getInstrumentation().getContext();
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mDevicePolicyManager = (DevicePolicyManager)
+                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
         mPackageManager = mContext.getPackageManager();
         mPackageInstaller = mPackageManager.getPackageInstaller();
         assertNotNull(mPackageInstaller);
@@ -79,7 +96,10 @@
 
     @Override
     protected void tearDown() throws Exception {
-        mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
+        if (mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME) ||
+                mDevicePolicyManager.isProfileOwnerApp(PACKAGE_NAME)) {
+            mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
+        }
         try {
             mContext.unregisterReceiver(mBroadcastReceiver);
         } catch (IllegalArgumentException e) {
@@ -91,33 +111,12 @@
         super.tearDown();
     }
 
-    public void testSilentInstallUninstall() throws Exception {
-        // install the app
-        assertInstallPackage();
-
-        // uninstall the app again
-        assertTrue(tryUninstallPackage());
-        assertFalse(isPackageInstalled(TEST_APP_PKG));
+    protected static ComponentName getWho() {
+        return new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName());
     }
 
-    public void testUninstallBlocked() throws Exception {
-        // install the app
-        assertInstallPackage();
-
-        mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, true);
-        assertTrue(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
-        assertTrue(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
-        assertFalse(tryUninstallPackage());
-        assertTrue(isPackageInstalled(TEST_APP_PKG));
-
-        mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
-        assertFalse(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
-        assertFalse(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
-        assertTrue(tryUninstallPackage());
+    protected void assertInstallPackage() throws Exception {
         assertFalse(isPackageInstalled(TEST_APP_PKG));
-    }
-
-    private void assertInstallPackage() throws Exception {
         synchronized (mPackageInstallerTimeoutLock) {
             mCallbackReceived = false;
             mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
@@ -134,7 +133,7 @@
         assertTrue(isPackageInstalled(TEST_APP_PKG));
     }
 
-    private boolean tryUninstallPackage() throws Exception {
+    protected boolean tryUninstallPackage() throws Exception {
         assertTrue(isPackageInstalled(TEST_APP_PKG));
         synchronized (mPackageInstallerTimeoutLock) {
             mCallbackReceived = false;
@@ -151,7 +150,7 @@
         }
     }
 
-    private void installPackage(String packageLocation) throws Exception {
+    protected void installPackage(String packageLocation) throws Exception {
         PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
         int sessionId = mPackageInstaller.createSession(params);
@@ -184,11 +183,11 @@
                 mContext,
                 sessionId,
                 broadcastIntent,
-                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+                PendingIntent.FLAG_UPDATE_CURRENT);
         return pendingIntent.getIntentSender();
     }
 
-    private boolean isPackageInstalled(String packageName) {
+    protected boolean isPackageInstalled(String packageName) {
         try {
             PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
             return pi != null;
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ClearDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ClearDeviceOwnerTest.java
new file mode 100644
index 0000000..a06271b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ClearDeviceOwnerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.packageinstaller;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Base class for profile and device based tests.
+ *
+ * This class handles making sure that the test is the profile or device owner and that it has an
+ * active admin registered, so that all tests may assume these are done.
+ */
+public class ClearDeviceOwnerTest extends InstrumentationTestCase {
+
+    public static class BasicAdminReceiver extends DeviceAdminReceiver {
+    }
+
+    public static final String PACKAGE_NAME = BasicAdminReceiver.class.getPackage().getName();
+    public static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
+            PACKAGE_NAME, BasicAdminReceiver.class.getName());
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mDevicePolicyManager = (DevicePolicyManager)
+                getInstrumentation().getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+        assertNotNull(mDevicePolicyManager);
+
+        assertTrue(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
+        assertTrue("App is not device owner", mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        removeActiveAdmin(ADMIN_RECEIVER_COMPONENT);
+        mDevicePolicyManager.clearDeviceOwnerApp(PACKAGE_NAME);
+        assertFalse(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
+        assertFalse(mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME));
+
+        super.tearDown();
+    }
+
+    // This test clears the device owner and active admin on tearDown(). To be called from the host
+    // side test once a test case is finished.
+    public void testClearDeviceOwner() {
+    }
+
+    private void removeActiveAdmin(ComponentName cn) throws InterruptedException {
+        if (mDevicePolicyManager.isAdminActive(cn)) {
+            mDevicePolicyManager.removeActiveAdmin(cn);
+            for (int i = 0; i < 1000 && mDevicePolicyManager.isAdminActive(cn); i++) {
+                Thread.sleep(100);
+            }
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
new file mode 100644
index 0000000..96affae
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.packageinstaller;
+
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+/**
+ * This class tests manual package install and uninstall by a device owner.
+ */
+public class ManualPackageInstallTest extends BasePackageInstallTest {
+    private static final int AUTOMATOR_WAIT_TIMEOUT = 5000;
+    private static final int INSTALL_WAIT_TIME = 5000;
+
+    private static final BySelector POPUP_BUTTON_SELECTOR = By
+            .clazz(android.widget.Button.class.getName())
+            .res("android:id/button1")
+            .pkg("com.google.android.packageinstaller");
+    private static final BySelector POPUP_TEXT_SELECTOR = By
+            .clazz(android.widget.TextView.class.getName())
+            .res("android:id/alertTitle")
+            .pkg("com.google.android.packageinstaller");
+    private static final BySelector INSTALL_BUTTON_SELECTOR = By
+            .clazz(android.widget.Button.class.getName())
+            .res("com.android.packageinstaller:id/ok_button")
+            .pkg("com.google.android.packageinstaller");
+
+    public void testManualInstallSucceeded() throws Exception {
+        assertInstallPackage();
+    }
+
+    public void testManualInstallBlocked() throws Exception {
+        synchronized (mPackageInstallerTimeoutLock) {
+            mCallbackReceived = false;
+            mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
+        }
+        // Calls the original installPackage which does not click through the install button.
+        super.installPackage(TEST_APP_LOCATION);
+        synchronized (mPackageInstallerTimeoutLock) {
+            try {
+                mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
+            } catch (InterruptedException e) {
+            }
+            assertTrue(mCallbackReceived);
+            assertEquals(PackageInstaller.STATUS_PENDING_USER_ACTION, mCallbackStatus);
+        }
+
+        mCallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(mCallbackIntent);
+
+        automateDismissInstallBlockedDialog();
+
+        // Assuming installation is not synchronous, we should wait a while before checking.
+        Thread.sleep(INSTALL_WAIT_TIME);
+        assertFalse(isPackageInstalled(TEST_APP_PKG));
+    }
+
+    @Override
+    protected void installPackage(String packageLocation) throws Exception {
+        super.installPackage(packageLocation);
+
+        synchronized (mPackageInstallerTimeoutLock) {
+            try {
+                mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
+            } catch (InterruptedException e) {
+            }
+            assertTrue(mCallbackReceived);
+            assertEquals(PackageInstaller.STATUS_PENDING_USER_ACTION, mCallbackStatus);
+        }
+
+        // Use a receiver to listen for package install.
+        synchronized (mPackageInstallerTimeoutLock) {
+            mCallbackReceived = false;
+            mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
+        }
+
+        mCallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(mCallbackIntent);
+
+        automateInstallClick();
+    }
+
+    private void automateInstallClick() {
+        mDevice.wait(Until.hasObject(INSTALL_BUTTON_SELECTOR), AUTOMATOR_WAIT_TIMEOUT);
+        UiObject2 button = mDevice.findObject(INSTALL_BUTTON_SELECTOR);
+        assertNotNull("Install button not found", button);
+        button.click();
+    }
+
+    private void automateDismissInstallBlockedDialog() {
+        mDevice.wait(Until.hasObject(POPUP_TEXT_SELECTOR), AUTOMATOR_WAIT_TIMEOUT);
+        UiObject2 text = mDevice.findObject(POPUP_TEXT_SELECTOR);
+        assertNotNull("Alert dialog not found", text);
+        // "OK" button only present in the dialog if it is blocked by policy.
+        UiObject2 button = mDevice.findObject(POPUP_BUTTON_SELECTOR);
+        assertNotNull("OK button not found", button);
+        button.click();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/SilentPackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/SilentPackageInstallTest.java
new file mode 100644
index 0000000..f1a80b9
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/SilentPackageInstallTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.packageinstaller;
+
+/**
+ * This class tests silent package install and uninstall by a device owner.
+ */
+public class SilentPackageInstallTest extends BasePackageInstallTest {
+    public void testSilentInstallUninstall() throws Exception {
+        // install the app
+        assertInstallPackage();
+
+        // uninstall the app again
+        assertTrue(tryUninstallPackage());
+        assertFalse(isPackageInstalled(TEST_APP_PKG));
+    }
+
+    public void testUninstallBlocked() throws Exception {
+        // install the app
+        assertInstallPackage();
+
+        mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, true);
+        assertTrue(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
+        assertTrue(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
+        assertFalse(tryUninstallPackage());
+        assertTrue(isPackageInstalled(TEST_APP_PKG));
+
+        mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
+        assertFalse(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
+        assertFalse(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
+        assertTrue(tryUninstallPackage());
+        assertFalse(isPackageInstalled(TEST_APP_PKG));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 650e963..4fc14e4 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -17,7 +17,6 @@
 package com.android.cts.devicepolicy;
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.InstrumentationResultParser;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
@@ -363,4 +362,20 @@
         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
         return commandOutput.startsWith("Success:");
     }
+
+    protected String getSettings(String namespace, String name, int userId)
+            throws DeviceNotAvailableException {
+        String command = "settings --user " + userId + " get " + namespace + " " + name;
+        String commandOutput = getDevice().executeShellCommand(command);
+        CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
+        return commandOutput.replace("\n", "").replace("\r", "");
+    }
+
+    protected void putSettings(String namespace, String name, String value, int userId)
+            throws DeviceNotAvailableException {
+        String command = "settings --user " + userId + " put " + namespace + " " + name
+                + " " + value;
+        String commandOutput = getDevice().executeShellCommand(command);
+        CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
index 7cb8f3b..8d22638 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
@@ -16,9 +16,7 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
+import java.io.File;
 import java.lang.Exception;
 
 /**
@@ -49,6 +47,18 @@
     private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
     private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
 
+    private static final String TEST_APP_APK = "CtsSimpleApp.apk";
+    private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+    private static final String TEST_APP_LOCATION = "/data/local/tmp/";
+
+    private static final String PACKAGE_INSTALLER_PKG = "com.android.cts.packageinstaller";
+    private static final String PACKAGE_INSTALLER_APK = "CtsPackageInstallerApp.apk";
+    private static final String PACKAGE_INSTALLER_ADMIN_COMPONENT =
+            PACKAGE_INSTALLER_PKG + "/" + ".ClearDeviceOwnerTest$BasicAdminReceiver";
+    private static final String PACKAGE_INSTALLER_CLEAR_DEVICE_OWNER_TEST_CLASS =
+            PACKAGE_INSTALLER_PKG + ".ClearDeviceOwnerTest";
+
+    @Override
     public void tearDown() throws Exception {
         if (mHasFeature) {
             getDevice().uninstallPackage(DEVICE_OWNER_PKG);
@@ -117,4 +127,28 @@
                     "testRemoveAccounts", 0));
         }
     }
+
+    public void testSilentPackageInstall() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
+        try {
+            // Install the test and prepare the test apk.
+            installApp(PACKAGE_INSTALLER_APK);
+            assertTrue(setDeviceOwner(PACKAGE_INSTALLER_ADMIN_COMPONENT));
+
+            getDevice().uninstallPackage(TEST_APP_PKG);
+            assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+            assertTrue(runDeviceTests(PACKAGE_INSTALLER_PKG,
+                    PACKAGE_INSTALLER_PKG + ".SilentPackageInstallTest"));
+        } finally {
+            assertTrue("Failed to remove device owner.", runDeviceTests(PACKAGE_INSTALLER_PKG,
+                    PACKAGE_INSTALLER_CLEAR_DEVICE_OWNER_TEST_CLASS));
+            String command = "rm " + TEST_APP_LOCATION + apk.getName();
+            String commandOutput = getDevice().executeShellCommand(command);
+            getDevice().uninstallPackage(TEST_APP_PKG);
+            getDevice().uninstallPackage(PACKAGE_INSTALLER_PKG);
+        }
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 36dab2c..43e6730 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -16,6 +16,12 @@
 
 package com.android.cts.devicepolicy;
 
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.io.File;
+
 /**
  * Set of tests for usecases that apply to profile and device owner.
  * This class is the base class of MixedProfileOwnerTest and MixedDeviceOwnerTest and is abstract
@@ -34,8 +40,21 @@
     private static final String SIMPLE_PRE_M_APP_PKG = "com.android.cts.launcherapps.simplepremapp";
     private static final String SIMPLE_PRE_M_APP_APK = "CtsSimplePreMApp.apk";
 
+    private static final String CERT_INSTALLER_PKG = "com.android.cts.certinstaller";
+    private static final String CERT_INSTALLER_APK = "CtsCertInstallerApp.apk";
+
+    private static final String TEST_APP_APK = "CtsSimpleApp.apk";
+    private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+    private static final String TEST_APP_LOCATION = "/data/local/tmp/";
+
+    private static final String PACKAGE_INSTALLER_PKG = "com.android.cts.packageinstaller";
+    private static final String PACKAGE_INSTALLER_APK = "CtsPackageInstallerApp.apk";
+
     protected static final int USER_OWNER = 0;
 
+    private static final String ADD_RESTRICTION_COMMAND = "add-restriction";
+    private static final String CLEAR_RESTRICTION_COMMAND = "clear-restriction";
+
     // ID of the user all tests are run as. For device owner this will be 0, for profile owner it
     // is the user id of the created profile.
     protected int mUserId;
@@ -46,6 +65,7 @@
             getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
             getDevice().uninstallPackage(PERMISSIONS_APP_PKG);
             getDevice().uninstallPackage(SIMPLE_PRE_M_APP_PKG);
+            getDevice().uninstallPackage(CERT_INSTALLER_PKG);
         }
         super.tearDown();
     }
@@ -159,13 +179,85 @@
         executeDeviceTestClass(".ApplicationHiddenTest");
     }
 
-    // TODO: Remove AccountManagementTest from XTS after GTS is released for MNC.
     public void testAccountManagement() throws Exception {
         if (!mHasFeature) {
             return;
         }
 
         executeDeviceTestClass(".AccountManagementTest");
+
+        // Send a home intent to dismiss an error dialog.
+        String command = "am start -a android.intent.action.MAIN -c android.intent.category.HOME";
+        CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": "
+                + getDevice().executeShellCommand(command));
+    }
+
+    public void testDelegatedCertInstaller() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(CERT_INSTALLER_APK, mUserId);
+        installAppAsUser(DEVICE_ADMIN_APK, USER_OWNER);
+        setDeviceAdmin(DEVICE_ADMIN_PKG + "/.PrimaryUserDeviceAdmin");
+
+        final String adminHelperClass = ".PrimaryUserAdminHelper";
+        try {
+            // Set a non-empty device lockscreen password, which is a precondition for installing
+            // private key pairs.
+            assertTrue("Set lockscreen password failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+                    adminHelperClass, "testSetPassword", 0 /* user 0 */));
+            assertTrue("DelegatedCertInstaller failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+                    ".DelegatedCertInstallerTest", mUserId));
+        } finally {
+            // Reset lockscreen password and remove device admin.
+            assertTrue("Clear lockscreen password failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+                    adminHelperClass, "testClearPassword", 0 /* user 0 */));
+            assertTrue("Clear device admin failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+                    adminHelperClass, "testClearDeviceAdmin", 0 /* user 0 */));
+        }
+    }
+
+    public void testPackageInstallUserRestrictions() throws Exception {
+        // UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES
+        final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
+        final String UNKNOWN_SOURCES_SETTING = "install_non_market_apps";
+        final String SECURE_SETTING_CATEGORY = "secure";
+        final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
+        String unknownSourceSetting = null;
+        try {
+            // Install the test and prepare the test apk.
+            installApp(PACKAGE_INSTALLER_APK);
+            assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+
+            // Add restrictions and test if we can install the apk.
+            getDevice().uninstallPackage(TEST_APP_PKG);
+            changeUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES,
+                    ADD_RESTRICTION_COMMAND, mUserId);
+            assertTrue(runDeviceTestsAsUser(PACKAGE_INSTALLER_PKG, ".ManualPackageInstallTest",
+                    "testManualInstallBlocked", mUserId));
+
+            // Clear restrictions and test if we can install the apk.
+            changeUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES,
+                    CLEAR_RESTRICTION_COMMAND, mUserId);
+
+            // Enable Unknown sources in Settings.
+            unknownSourceSetting =
+                    getSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, mUserId);
+            putSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, "1", mUserId);
+            assertEquals("1",
+                    getSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, mUserId));
+            assertTrue(runDeviceTestsAsUser(PACKAGE_INSTALLER_PKG, ".ManualPackageInstallTest",
+                    "testManualInstallSucceeded", mUserId));
+        } finally {
+            String command = "rm " + TEST_APP_LOCATION + apk.getName();
+            getDevice().executeShellCommand(command);
+            getDevice().uninstallPackage(TEST_APP_PKG);
+            getDevice().uninstallPackage(PACKAGE_INSTALLER_APK);
+            if (unknownSourceSetting != null) {
+                putSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, unknownSourceSetting,
+                        mUserId);
+            }
+        }
     }
 
     protected void executeDeviceTestClass(String className) throws Exception {
@@ -175,4 +267,18 @@
     protected void executeDeviceTestMethod(String className, String testName) throws Exception {
         assertTrue(runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, testName, mUserId));
     }
+
+    private void changeUserRestrictionForUser(String key, String command, int userId)
+            throws DeviceNotAvailableException {
+        String adbCommand = "am start -W --user " + userId
+                + " -c android.intent.category.DEFAULT "
+                + " --es extra-command " + command
+                + " --es extra-restriction-key " + key
+                + " " + DEVICE_ADMIN_PKG + "/.UserRestrictionActivity";
+        String commandOutput = getDevice().executeShellCommand(adbCommand);
+        CLog.logAndDisplay(LogLevel.INFO,
+                "Output for command " + adbCommand + ": " + commandOutput);
+        assertTrue("Command was expected to succeed " + commandOutput,
+                commandOutput.contains("Status: ok"));
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 4f267d1..96ca469 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -16,12 +16,6 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.io.File;
-
 /**
  * Set of tests for Device Owner use cases.
  */
@@ -35,10 +29,6 @@
     private static final String MANAGED_PROFILE_ADMIN =
             MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
 
-    private static final String TEST_APP_APK = "CtsSimpleApp.apk";
-    private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
-    private static final String TEST_APP_LOCATION = "/data/local/tmp/";
-
     private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
     private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
 
@@ -93,19 +83,6 @@
         }
     }
 
-    public void testPackageInstall() throws Exception {
-        final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
-        try {
-            getDevice().uninstallPackage(TEST_APP_PKG);
-            assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
-            executeDeviceOwnerTest("PackageInstallTest");
-        } finally {
-            String command = "rm " + TEST_APP_LOCATION + apk.getName();
-            String commandOutput = getDevice().executeShellCommand(command);
-            getDevice().uninstallPackage(TEST_APP_PKG);
-        }
-    }
-
     public void testSystemUpdatePolicy() throws Exception {
         executeDeviceOwnerTest("SystemUpdatePolicyTest");
     }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
index 8da189f..1d5dd11 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
@@ -25,11 +25,13 @@
  * apps.
  */
 public class LauncherAppsMultiUserTest extends BaseLauncherAppsTest {
+    private static final String FEATURE_LIVE_TV = "android.software.live_tv";
 
     private int mSecondaryUserId;
     private int mSecondaryUserSerialNumber;
 
     private boolean mMultiUserSupported;
+    private boolean mHasLiveTvFeature;
 
     @Override
     protected void setUp() throws Exception {
@@ -37,6 +39,7 @@
         // We need multi user to be supported in order to create a secondary user
         // and api level 21 to support LauncherApps
         mMultiUserSupported = getMaxNumberOfUsersSupported() > 1 && getDevice().getApiLevel() >= 21;
+        mHasLiveTvFeature = hasDeviceFeature(FEATURE_LIVE_TV);
 
         if (mMultiUserSupported) {
             removeTestUsers();
@@ -58,7 +61,7 @@
     }
 
     public void testGetActivitiesForNonProfileFails() throws Exception {
-        if (!mMultiUserSupported) {
+        if (!mMultiUserSupported || mHasLiveTvFeature) {
             return;
         }
         installApp(SIMPLE_APP_APK);
@@ -73,7 +76,7 @@
     }
 
     public void testNoLauncherCallbackPackageAddedSecondaryUser() throws Exception {
-        if (!mMultiUserSupported) {
+        if (!mMultiUserSupported || mHasLiveTvFeature) {
             return;
         }
         startCallbackService();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index acc5b26..214131f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -42,9 +42,6 @@
     private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
     private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
 
-    private static final String CERT_INSTALLER_PKG = "com.android.cts.certinstaller";
-    private static final String CERT_INSTALLER_APK = "CtsCertInstallerApp.apk";
-
     private static final String WIFI_CONFIG_CREATOR_PKG = "com.android.cts.wificonfigcreator";
     private static final String WIFI_CONFIG_CREATOR_APK = "CtsWifiConfigCreator.apk";
 
@@ -94,7 +91,6 @@
             getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
             getDevice().uninstallPackage(INTENT_SENDER_PKG);
             getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
-            getDevice().uninstallPackage(CERT_INSTALLER_PKG);
         }
         super.tearDown();
     }
@@ -525,28 +521,6 @@
                 "testSetBluetoothContactSharingDisabled_setterAndGetter", mUserId));
     }
 
-    public void testDelegatedCertInstaller() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        installApp(CERT_INSTALLER_APK);
-        setDeviceAdmin(MANAGED_PROFILE_PKG + "/.PrimaryUserDeviceAdmin");
-
-        final String adminHelperClass = ".PrimaryUserAdminHelper";
-        try {
-            assertTrue("Set lockscreen password failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    adminHelperClass, "testSetPassword", 0 /* user 0 */));
-            assertTrue("DelegatedCertInstaller failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    ".DelegatedCertInstallerTest", mUserId));
-        } finally {
-            // Reset lockscreen password and remove device admin.
-            assertTrue("Clear lockscreen password failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    adminHelperClass, "testClearPassword", 0 /* user 0 */));
-            assertTrue("Clear device admin failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    adminHelperClass, "testClearDeviceAdmin", 0 /* user 0 */));
-        }
-    }
-
     public void testCannotSetProfileOwnerAgain() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/dumpsys/FramestatsTestApp/Android.mk b/hostsidetests/dumpsys/FramestatsTestApp/Android.mk
new file mode 100644
index 0000000..1104523
--- /dev/null
+++ b/hostsidetests/dumpsys/FramestatsTestApp/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_PACKAGE_NAME := CtsFramestatsTestApp
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/dumpsys/FramestatsTestApp/AndroidManifest.xml b/hostsidetests/dumpsys/FramestatsTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..3a9f902
--- /dev/null
+++ b/hostsidetests/dumpsys/FramestatsTestApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+       package="com.android.cts.framestatstestapp">
+    <!--
+    A simple app that draws at least one frame. Used by framestats
+    test.
+    -->
+    <application>
+        <activity android:name=".FramestatsTestAppActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/dumpsys/FramestatsTestApp/src/com/android/cts/framestatstestapp/FramestatsTestAppActivity.java b/hostsidetests/dumpsys/FramestatsTestApp/src/com/android/cts/framestatstestapp/FramestatsTestAppActivity.java
new file mode 100644
index 0000000..7370508
--- /dev/null
+++ b/hostsidetests/dumpsys/FramestatsTestApp/src/com/android/cts/framestatstestapp/FramestatsTestAppActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.framestatstestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.View;
+
+public class FramestatsTestAppActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View v = new View(this);
+        v.setBackgroundColor(0xFF00FF00);
+        setContentView(v);
+    }
+}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index a787cdd..0daae03 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -16,11 +16,15 @@
 
 package android.dumpsys.cts;
 
-import com.android.ddmlib.Log;
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
 
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
 import java.io.StringReader;
 import java.util.HashSet;
 import java.util.Set;
@@ -28,8 +32,10 @@
 /**
  * Test to check the format of the dumps of various services (currently only procstats is tested).
  */
-public class DumpsysHostTest extends DeviceTestCase {
+public class DumpsysHostTest extends DeviceTestCase implements IBuildReceiver {
     private static final String TAG = "DumpsysHostTest";
+    private static final String TEST_APK = "CtsFramestatsTestApp.apk";
+    private static final String TEST_PKG = "com.android.cts.framestatstestapp";
 
     /**
      * A reference to the device under test.
@@ -815,17 +821,38 @@
      */
     public void testGfxinfoFramestats() throws Exception {
         final String MARKER = "---PROFILEDATA---";
-        final int TIMESTAMP_COUNT = 14;
 
-        String frameinfo = mDevice.executeShellCommand("dumpsys gfxinfo com.android.systemui framestats");
-        assertNotNull(frameinfo);
-        assertTrue(frameinfo.length() > 0);
-        int profileStart = frameinfo.indexOf(MARKER);
-        int profileEnd = frameinfo.indexOf(MARKER, profileStart + 1);
-        assertTrue(profileStart >= 0);
-        assertTrue(profileEnd > profileStart);
-        String profileData = frameinfo.substring(profileStart + MARKER.length(), profileEnd);
-        assertTrue(profileData.length() > 0);
+        try {
+            // cleanup test apps that might be installed from previous partial test run
+            getDevice().uninstallPackage(TEST_PKG);
+
+            // install the test app
+            File testAppFile = mCtsBuild.getTestApp(TEST_APK);
+            String installResult = getDevice().installPackage(testAppFile, false);
+            assertNull(
+                    String.format("failed to install atrace test app. Reason: %s", installResult),
+                    installResult);
+
+            getDevice().executeShellCommand("am start -W " + TEST_PKG);
+
+            String frameinfo = mDevice.executeShellCommand("dumpsys gfxinfo " +
+                    TEST_PKG + " framestats");
+            assertNotNull(frameinfo);
+            assertTrue(frameinfo.length() > 0);
+            int profileStart = frameinfo.indexOf(MARKER);
+            int profileEnd = frameinfo.indexOf(MARKER, profileStart + 1);
+            assertTrue(profileStart >= 0);
+            assertTrue(profileEnd > profileStart);
+            String profileData = frameinfo.substring(profileStart + MARKER.length(), profileEnd);
+            assertTrue(profileData.length() > 0);
+            validateProfileData(profileData);
+        } finally {
+            getDevice().uninstallPackage(TEST_PKG);
+        }
+    }
+
+    private void validateProfileData(String profileData) throws IOException {
+        final int TIMESTAMP_COUNT = 14;
         boolean foundAtLeastOneRow = false;
         try (BufferedReader reader = new BufferedReader(
                 new StringReader(profileData))) {
@@ -872,6 +899,16 @@
         assertTrue(foundAtLeastOneRow);
     }
 
+    private CtsBuildHelper mCtsBuild;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
     private static long assertInteger(String input) {
         try {
             return Long.parseLong(input);
diff --git a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
index a0d3167..abb1ac4 100644
--- a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
@@ -589,9 +589,9 @@
         assertDomainN("u:r:zygote:s0", "zygote", "zygote64");
     }
 
-    /* drm server is always present */
+    /* Checks drmserver for devices that require it */
     public void testDrmServerDomain() throws DeviceNotAvailableException {
-        assertDomainOne("u:r:drmserver:s0", "/system/bin/drmserver");
+        assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver");
     }
 
     /* Media server is always running */
diff --git a/hostsidetests/theme/Android.mk b/hostsidetests/theme/Android.mk
index 71027c7..188bf7a 100644
--- a/hostsidetests/theme/Android.mk
+++ b/hostsidetests/theme/Android.mk
@@ -29,6 +29,8 @@
 
 LOCAL_CTS_TEST_PACKAGE := android.host.theme
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/theme/README b/hostsidetests/theme/README
new file mode 100644
index 0000000..bce711a
--- /dev/null
+++ b/hostsidetests/theme/README
@@ -0,0 +1,73 @@
+* 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.
+
+
+INTRODUCTION
+
+The Android theme tests ensure that the Holo and Material themes have not been
+modified. They consist of API-specific sets of reference images representing
+specific themes and widgets that must be identical across devices. To pass the
+theme tests, a device must be able to generate images that are identical to the
+reference images.
+
+NOTE: Reference images should only be updated by the CTS test maintainers. Any
+      modifications to the reference images will invalidate the test results.
+
+
+INSTRUCTIONS
+
+I. Generating reference images (CTS maintainers only)
+
+Reference images are typically only generated for new API revisions. To
+generate a new set of reference images, do the following:
+
+  1. Connect one device for each DPI bucket (ldpi, xxxhdpi, etc.) that you wish
+     to generate references images for. Confirm that all devices are connected
+     with:
+
+     adb devices
+
+  2. Image generation occurs on all devices in parallel. Resulting sets of
+     reference images are saved in assets/<api>/<dpi>.zip and will overwrite
+     any existing sets. Image generation may be started using:
+
+     .cts/hostsidetests/theme/generate_images.sh
+
+A complete collection of reference images for a given API revision must include
+a set for each possible DPI bucket (tvdpi, xxhdpi, etc.) that may be tested.
+
+
+II. Building theme tests
+
+1. If you have not already built the CTS tests, run an initial make:
+
+   make cts -j32
+
+2. Subsequent changes to the theme tests, including changes to the reference
+   images, may be built using mmm:
+
+   mmm cts/hostsidetests/theme -j32
+
+
+III. Running theme tests
+
+1. Connect the device that you wish to test. Confirm that is is connected with:
+
+   adb devices
+
+2. Run the theme tests using cts-tradefed:
+
+   cts-tradefed run cts -c android.theme.cts.ThemeHostTest
+
+3. Wait for the tests to complete. This should take less than five minutes.
diff --git a/hostsidetests/theme/android_device.py b/hostsidetests/theme/android_device.py
new file mode 100644
index 0000000..97b5fdd
--- /dev/null
+++ b/hostsidetests/theme/android_device.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import os
+import re
+import sys
+import threading
+import subprocess
+import time
+
+# class for running android device from python
+# it will fork the device processor
+class androidDevice(object):
+    def __init__(self, adbDevice):
+        self._adbDevice = adbDevice
+
+    def runAdbCommand(self, cmd):
+        self.waitForAdbDevice()
+        adbCmd = "adb -s %s %s" %(self._adbDevice, cmd)
+        adbProcess = subprocess.Popen(adbCmd.split(" "), bufsize = -1, stdout = subprocess.PIPE)
+        return adbProcess.communicate()
+
+    def runShellCommand(self, cmd):
+        return self.runAdbCommand("shell " + cmd)
+
+    def waitForAdbDevice(self):
+        os.system("adb -s %s wait-for-device" %self._adbDevice)
+
+    def waitForBootComplete(self, timeout = 240):
+        boot_complete = False
+        attempts = 0
+        wait_period = 5
+        while not boot_complete and (attempts*wait_period) < timeout:
+            (output, err) = self.runShellCommand("getprop dev.bootcomplete")
+            output = output.strip()
+            if output == "1":
+                boot_complete = True
+            else:
+                time.sleep(wait_period)
+                attempts += 1
+        if not boot_complete:
+            print "***boot not complete within timeout. will proceed to the next step"
+        return boot_complete
+
+    def installApk(self, apkPath):
+        (out, err) = self.runAdbCommand("install -r -d -g " + apkPath)
+        result = out.split()
+        return (out, err, "Success" in result)
+
+    def uninstallApk(self, package):
+        (out, err) = self.runAdbCommand("uninstall " + package)
+        result = out.split()
+        return "Success" in result
+
+    def runInstrumentationTest(self, option):
+        return self.runShellCommand("am instrument -w " + option)
+
+    def isProcessAlive(self, processName):
+        (out, err) = self.runShellCommand("ps")
+        names = out.split()
+        # very lazy implementation as it does not filter out things like uid
+        # should work mostly unless processName is too simple to overlap with
+        # uid. So only use name like com.android.xyz
+        return processName in names
+
+    def getDensity(self):
+        if "emulator" in self._adbDevice:
+          return int(self.runShellCommand("getprop qemu.sf.lcd_density")[0])
+        else:
+          return int(self.runShellCommand("getprop ro.sf.lcd_density")[0])
+
+    def getSdkLevel(self):
+        return int(self.runShellCommand("getprop ro.build.version.sdk")[0])
+
+    def getOrientation(self):
+        return int(self.runShellCommand("dumpsys | grep SurfaceOrientation")[0].split()[1])
+
+def runAdbDevices():
+    devices = subprocess.check_output(["adb", "devices"])
+    devices = devices.split('\n')[1:]
+
+    deviceSerial = []
+
+    for device in devices:
+        if device is not "":
+            info = device.split('\t')
+            if info[1] == "device":
+                deviceSerial.append(info[0])
+
+    return deviceSerial
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/hostsidetests/theme/app/Android.mk b/hostsidetests/theme/app/Android.mk
index 70623cb..1be2983 100644
--- a/hostsidetests/theme/app/Android.mk
+++ b/hostsidetests/theme/app/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 #Flags to tell the Android Asset Packaging Tool not to strip for some densities
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index 81a4d9d..4e81ab0 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -22,9 +22,10 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
     <application>
         <uses-library android:name="android.test.runner" />
-        <activity android:name=".HoloDeviceActivity">
+        <activity android:name=".ThemeDeviceActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -37,6 +38,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".GenerateImagesActivity"
+                  android:exported="true" />
     </application>
 
+    <!--  self-instrumenting test package. -->
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.theme.app"
+                     android:label="Generates Theme reference images"/>
+
 </manifest>
diff --git a/hostsidetests/theme/app/res/layout/holo_test.xml b/hostsidetests/theme/app/res/layout/theme_test.xml
similarity index 100%
rename from hostsidetests/theme/app/res/layout/holo_test.xml
rename to hostsidetests/theme/app/res/layout/theme_test.xml
diff --git a/hostsidetests/theme/app/res/values/strings.xml b/hostsidetests/theme/app/res/values/strings.xml
index a69a2e0..d9d6602 100644
--- a/hostsidetests/theme/app/res/values/strings.xml
+++ b/hostsidetests/theme/app/res/values/strings.xml
@@ -14,8 +14,6 @@
      limitations under the License.
 -->
 <resources>
-    <string name="holo_test_utilities">Holo Test Utilities</string>
-
     <string name="display_info">Display Info</string>
     <string name="display_info_text">Density DPI: %1$d\nDensity Bucket: %2$s\nWidth DP: %3$d\nHeight DP: %4$d</string>
 
diff --git a/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java b/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
index 5255698..530675d 100644
--- a/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
@@ -25,7 +25,8 @@
 import android.widget.TextView;
 
 /**
- * An activity to display information about the device, including density bucket and dimensions.
+ * An activity to display information about the device, including density
+ * bucket and dimensions.
  */
 public class DisplayInfoActivity extends Activity {
 
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java b/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java
new file mode 100644
index 0000000..05b6dcf
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Canvas;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+/**
+ * A task which gets the UI element to render to a bitmap and then saves that
+ * as a PNG asynchronously.
+ */
+class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
+    private static final String TAG = "GenerateBitmapTask";
+
+    private final View mView;
+    private final File mOutDir;
+
+    private Bitmap mBitmap;
+
+    protected final String mName;
+
+    public GenerateBitmapTask(View view, File outDir, String name) {
+        mView = view;
+        mOutDir = outDir;
+        mName = name;
+    }
+
+    @Override
+    protected void onPreExecute() {
+        if (mView.getWidth() == 0 || mView.getHeight() == 0) {
+            Log.e(TAG, "Unable to draw view due to incorrect size: " + mName);
+            return;
+        }
+
+        mBitmap = Bitmap.createBitmap(mView.getWidth(), mView.getHeight(),
+                Bitmap.Config.ARGB_8888);
+
+        final Canvas canvas = new Canvas(mBitmap);
+        mView.draw(canvas);
+    }
+
+    @Override
+    protected Boolean doInBackground(Void... ignored) {
+        final Bitmap bitmap = mBitmap;
+        if (bitmap == null) {
+            return false;
+        }
+
+        final File file = new File(mOutDir, mName + ".png");
+        if (file.exists() && !file.canWrite()) {
+            Log.e(TAG, "Unable to write file: " + file.getAbsolutePath());
+            return false;
+        }
+
+        boolean success = false;
+        try {
+            FileOutputStream stream = null;
+            try {
+                stream = new FileOutputStream(file);
+                success = bitmap.compress(CompressFormat.PNG, 100, stream);
+            } finally {
+                if (stream != null) {
+                    stream.flush();
+                    stream.close();
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, e.getMessage());
+        } finally {
+            bitmap.recycle();
+        }
+
+        return success;
+    }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
new file mode 100644
index 0000000..e7f5aa2
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build.VERSION;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.util.Log;
+import android.view.WindowManager.LayoutParams;
+
+import java.io.File;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Generates images by iterating through all themes and launching instances of
+ * {@link ThemeDeviceActivity}.
+ */
+public class GenerateImagesActivity extends Activity {
+    private static final String TAG = "GenerateImagesActivity";
+
+    private static final String OUT_DIR = "cts-theme-assets";
+    private static final int REQUEST_CODE = 1;
+
+    private final CountDownLatch mLatch = new CountDownLatch(1);
+
+    private File mOutputDir;
+    private int mCurrentTheme;
+    private String mFinishReason;
+    private boolean mFinishSuccess;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
+                | LayoutParams.FLAG_TURN_SCREEN_ON
+                | LayoutParams.FLAG_DISMISS_KEYGUARD);
+
+        mOutputDir = new File(Environment.getExternalStorageDirectory(), OUT_DIR);
+        ThemeTestUtils.deleteDirectory(mOutputDir);
+        mOutputDir.mkdirs();
+
+        if (!mOutputDir.exists()) {
+            finish("Failed to create output directory " + mOutputDir.getAbsolutePath(), false);
+            return;
+        }
+
+        final boolean canDisableKeyguard = checkCallingOrSelfPermission(
+                permission.DISABLE_KEYGUARD) == PackageManager.PERMISSION_GRANTED;
+        if (!canDisableKeyguard) {
+            finish("Not granted permission to disable keyguard", false);
+            return;
+        }
+
+        new KeyguardCheck(this) {
+            @Override
+            public void onSuccess() {
+                generateNextImage();
+            }
+
+            @Override
+            public void onFailure() {
+                finish("Device is locked", false);
+            }
+        }.start();
+    }
+
+    private void finish(String reason, boolean success) {
+        mFinishSuccess = success;
+        mFinishReason = reason;
+
+        Log.i(TAG, (success ? "OKAY" : "FAIL") + ":" + reason);
+        finish();
+    }
+
+    public boolean isFinishSuccess() {
+        return mFinishSuccess;
+    }
+
+    public String getFinishReason() {
+        return mFinishReason;
+    }
+
+    static abstract class KeyguardCheck implements Runnable {
+        private static final int MAX_RETRIES = 3;
+        private static final int RETRY_DELAY = 500;
+
+        private final Handler mHandler;
+        private final KeyguardManager mKeyguard;
+
+        private int mRetries;
+
+        public KeyguardCheck(Context context) {
+            mHandler = new Handler(context.getMainLooper());
+            mKeyguard = (KeyguardManager) context.getSystemService(KEYGUARD_SERVICE);
+        }
+
+        public void start() {
+            mRetries = 0;
+
+            mHandler.removeCallbacks(this);
+            mHandler.post(this);
+        }
+
+        public void cancel() {
+            mHandler.removeCallbacks(this);
+        }
+
+        @Override
+        public void run() {
+            if (!mKeyguard.isKeyguardLocked()) {
+                onSuccess();
+            } else if (mRetries < MAX_RETRIES) {
+                mRetries++;
+                mHandler.postDelayed(this, RETRY_DELAY);
+            } else {
+                onFailure();
+            }
+
+        }
+
+        public abstract void onSuccess();
+        public abstract void onFailure();
+    }
+
+    public File getOutputDir() {
+        return mOutputDir;
+    }
+
+    /**
+     * Starts the activity to generate the next image.
+     */
+    private boolean generateNextImage() {
+        final ThemeDeviceActivity.Theme theme = ThemeDeviceActivity.THEMES[mCurrentTheme];
+        if (theme.apiLevel > VERSION.SDK_INT) {
+            Log.v(TAG, "Skipping theme \"" + theme.name
+                    + "\" (requires API " + theme.apiLevel + ")");
+            return false;
+        }
+
+        Log.v(TAG, "Generating images for theme \"" + theme.name + "\"...");
+
+        final Intent intent = new Intent(this, ThemeDeviceActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        intent.putExtra(ThemeDeviceActivity.EXTRA_THEME, mCurrentTheme);
+        intent.putExtra(ThemeDeviceActivity.EXTRA_OUTPUT_DIR, mOutputDir.getAbsolutePath());
+        startActivityForResult(intent, REQUEST_CODE);
+        return true;
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode != RESULT_OK) {
+            Log.i(TAG, "FAIL:Failed to generate images for theme " + mCurrentTheme);
+            finish();
+            return;
+        }
+
+        // Keep trying themes until one works.
+        boolean success = false;
+        while (++mCurrentTheme < ThemeDeviceActivity.THEMES.length && !success) {
+            success = generateNextImage();
+        }
+
+        // If we ran out of themes, we're done.
+        if (!success) {
+            finish("Image generation complete!", true);
+        }
+    }
+
+    public void finish() {
+        mLatch.countDown();
+        super.finish();
+    }
+
+    public void waitForCompletion() throws InterruptedException {
+        mLatch.await();
+    }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
deleted file mode 100644
index 8ae9fc8..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.theme.app;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Canvas;
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.os.Bundle;
-import android.os.Handler;
-import android.theme.app.modifiers.DatePickerModifier;
-import android.theme.app.modifiers.ProgressBarModifier;
-import android.theme.app.modifiers.SearchViewModifier;
-import android.theme.app.modifiers.TimePickerModifier;
-import android.theme.app.modifiers.ViewCheckedModifier;
-import android.theme.app.modifiers.ViewPressedModifier;
-import android.theme.app.R;
-import android.theme.app.ReferenceViewGroup;
-import android.util.Log;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.DatePicker;
-import android.widget.LinearLayout;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.Override;
-
-/**
- * A activity which display various UI elements with Holo theme.
- */
-public class HoloDeviceActivity extends Activity {
-
-    public static final String EXTRA_THEME = "holo_theme_extra";
-
-    private static final String TAG = HoloDeviceActivity.class.getSimpleName();
-
-    /**
-     * The duration of the CalendarView adjustement to settle to its final position.
-     */
-    private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
-
-    private Theme mTheme;
-
-    private ReferenceViewGroup mViewGroup;
-
-    private int mLayoutIndex;
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        mTheme = THEMES[getIntent().getIntExtra(EXTRA_THEME, 0)];
-        setTheme(mTheme.mId);
-        setContentView(R.layout.holo_test);
-        mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        setNextLayout();
-    }
-
-    @Override
-    protected void onPause() {
-        if (!isFinishing()) {
-            // The Activity got paused for some reasons, for finish it as the host won't move on to
-            // the next theme otherwise.
-            Log.w(TAG, "onPause called without a call to finish().");
-            finish();
-        }
-        super.onPause();
-    }
-
-    @Override
-    protected void onDestroy() {
-        if (mLayoutIndex != LAYOUTS.length) {
-            Log.w(TAG, "Not all layouts got rendered: " + mLayoutIndex);
-        }
-        Log.i(TAG, "OKAY:" + mTheme.mName);
-        super.onDestroy();
-    }
-
-    /**
-     * Sets the next layout in the UI.
-     */
-    private void setNextLayout() {
-        if (mLayoutIndex >= LAYOUTS.length) {
-            finish();
-            return;
-        }
-        final Layout layout = LAYOUTS[mLayoutIndex++];
-        final String layoutName = String.format("%s_%s", mTheme.mName, layout.mName);
-
-        mViewGroup.removeAllViews();
-        final View view = getLayoutInflater().inflate(layout.mId, mViewGroup, false);
-        if (layout.mModifier != null) {
-            layout.mModifier.modifyView(view);
-        }
-        mViewGroup.addView(view);
-        view.setFocusable(false);
-
-        final Runnable generateBitmapRunnable = new Runnable() {
-            @Override
-            public void run() {
-                new GenerateBitmapTask(view, layoutName).execute();
-            }
-        };
-
-        if (view instanceof DatePicker) {
-            // DatePicker uses a CalendarView that has a non-configurable adjustment duration of
-            // 540ms
-            view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
-        } else {
-            view.post(generateBitmapRunnable);
-        }
-    }
-
-    /**
-     * A task which gets the UI element to render to a bitmap and then saves that as a png
-     * asynchronously
-     */
-    private class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
-
-        private final View mView;
-
-        private final String mName;
-
-        public GenerateBitmapTask(final View view, final String name) {
-            super();
-            mView = view;
-            mName = name;
-        }
-
-        @Override
-        protected Boolean doInBackground(Void... ignored) {
-            if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
-                Log.i(TAG, "External storage for saving bitmaps is not mounted");
-                return false;
-            }
-            if (mView.getWidth() == 0 || mView.getHeight() == 0) {
-                Log.w(TAG, "Unable to draw View due to incorrect size: " + mName);
-                return false;
-            }
-
-            final Bitmap bitmap = Bitmap.createBitmap(
-                    mView.getWidth(), mView.getHeight(), Bitmap.Config.ARGB_8888);
-            final Canvas canvas = new Canvas(bitmap);
-
-            mView.draw(canvas);
-            final File dir = new File(Environment.getExternalStorageDirectory(), "cts-holo-assets");
-            dir.mkdirs();
-            boolean success = false;
-            try {
-                final File file = new File(dir, mName + ".png");
-                FileOutputStream stream = null;
-                try {
-                    stream = new FileOutputStream(file);
-                    success = bitmap.compress(CompressFormat.PNG, 100, stream);
-                } finally {
-                    if (stream != null) {
-                        stream.close();
-                    }
-                }
-            } catch (Exception e) {
-                Log.e(TAG, e.getMessage());
-            } finally {
-                bitmap.recycle();
-            }
-            return success;
-        }
-
-        @Override
-        protected void onPostExecute(Boolean success) {
-            setNextLayout();
-        }
-    }
-
-    /**
-     * A class to encapsulate information about a holo theme.
-     */
-    private static class Theme {
-
-        public final int mId;
-
-        public final String mName;
-
-        private Theme(int id, String name) {
-            mId = id;
-            mName = name;
-        }
-    }
-
-    private static final Theme[] THEMES = {
-            new Theme(android.R.style.Theme_Holo,
-                    "holo"),
-            new Theme(android.R.style.Theme_Holo_Dialog,
-                    "holo_dialog"),
-            new Theme(android.R.style.Theme_Holo_Dialog_MinWidth,
-                    "holo_dialog_minwidth"),
-            new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar,
-                    "holo_dialog_noactionbar"),
-            new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
-                    "holo_dialog_noactionbar_minwidth"),
-            new Theme(android.R.style.Theme_Holo_DialogWhenLarge,
-                    "holo_dialogwhenlarge"),
-            new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
-                    "holo_dialogwhenlarge_noactionbar"),
-            new Theme(android.R.style.Theme_Holo_InputMethod,
-                    "holo_inputmethod"),
-            new Theme(android.R.style.Theme_Holo_Light,
-                    "holo_light"),
-            new Theme(android.R.style.Theme_Holo_Light_DarkActionBar,
-                    "holo_light_darkactionbar"),
-            new Theme(android.R.style.Theme_Holo_Light_Dialog,
-                    "holo_light_dialog"),
-            new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth,
-                    "holo_light_dialog_minwidth"),
-            new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
-                    "holo_light_dialog_noactionbar"),
-            new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
-                    "holo_light_dialog_noactionbar_minwidth"),
-            new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge,
-                    "holo_light_dialogwhenlarge"),
-            new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
-                    "holo_light_dialogwhenlarge_noactionbar"),
-            new Theme(android.R.style.Theme_Holo_Light_NoActionBar,
-                    "holo_light_noactionbar"),
-            new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
-                    "holo_light_noactionbar_fullscreen"),
-            new Theme(android.R.style.Theme_Holo_Light_Panel,
-                    "holo_light_panel"),
-            new Theme(android.R.style.Theme_Holo_NoActionBar,
-                    "holo_noactionbar"),
-            new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen,
-                    "holo_noactionbar_fullscreen"),
-            new Theme(android.R.style.Theme_Holo_Panel,
-                    "holo_panel"),
-            new Theme(android.R.style.Theme_Holo_Wallpaper,
-                    "holo_wallpaper"),
-            new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
-                    "holo_wallpaper_notitlebar")
-    };
-
-    /**
-     * A class to encapsulate information about a holo layout.
-     */
-    private static class Layout {
-
-        public final int mId;
-
-        public final String mName;
-
-        public final LayoutModifier mModifier;
-
-        private Layout(int id, String name, LayoutModifier modifier) {
-            mId = id;
-            mName = name;
-            mModifier = modifier;
-        }
-    }
-
-    private static final Layout[] LAYOUTS = {
-            new Layout(R.layout.button, "button", null),
-            new Layout(R.layout.button, "button_pressed", new ViewPressedModifier()),
-            new Layout(R.layout.checkbox, "checkbox", null),
-            new Layout(R.layout.checkbox, "checkbox_checked", new ViewCheckedModifier()),
-            new Layout(R.layout.chronometer, "chronometer", null),
-            new Layout(R.layout.color_blue_bright, "color_blue_bright", null),
-            new Layout(R.layout.color_blue_dark, "color_blue_dark", null),
-            new Layout(R.layout.color_blue_light, "color_blue_light", null),
-            new Layout(R.layout.color_green_dark, "color_green_dark", null),
-            new Layout(R.layout.color_green_light, "color_green_light", null),
-            new Layout(R.layout.color_orange_dark, "color_orange_dark", null),
-            new Layout(R.layout.color_orange_light, "color_orange_light", null),
-            new Layout(R.layout.color_purple, "color_purple", null),
-            new Layout(R.layout.color_red_dark, "color_red_dark", null),
-            new Layout(R.layout.color_red_light, "color_red_light", null),
-            new Layout(R.layout.datepicker, "datepicker", new DatePickerModifier()),
-            new Layout(R.layout.display_info, "display_info", null),
-            new Layout(R.layout.edittext, "edittext", null),
-            new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0", null),
-            new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100", null),
-            new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50", null),
-            new Layout(R.layout.progressbar_large, "progressbar_large", new ProgressBarModifier()),
-            new Layout(R.layout.progressbar_small, "progressbar_small", new ProgressBarModifier()),
-            new Layout(R.layout.progressbar, "progressbar", new ProgressBarModifier()),
-            new Layout(R.layout.radiobutton_checked, "radiobutton_checked", null),
-            new Layout(R.layout.radiobutton, "radiobutton", null),
-            new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal", null),
-            new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical", null),
-            new Layout(R.layout.ratingbar_0, "ratingbar_0", null),
-            new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5", null),
-            new Layout(R.layout.ratingbar_5, "ratingbar_5", null),
-            new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed", new ViewPressedModifier()),
-            new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed", new ViewPressedModifier()),
-            new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed", new ViewPressedModifier()),
-            new Layout(R.layout.searchview, "searchview_query", new SearchViewModifier(SearchViewModifier.QUERY)),
-            new Layout(R.layout.searchview, "searchview_query_hint", new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
-            new Layout(R.layout.seekbar_0, "seekbar_0", null),
-            new Layout(R.layout.seekbar_100, "seekbar_100", null),
-            new Layout(R.layout.seekbar_50, "seekbar_50", null),
-            new Layout(R.layout.spinner, "spinner", null),
-            new Layout(R.layout.switch_button_checked, "switch_button_checked", null),
-            new Layout(R.layout.switch_button, "switch_button", null),
-            new Layout(R.layout.textview, "textview", null),
-            new Layout(R.layout.timepicker, "timepicker", new TimePickerModifier()),
-            new Layout(R.layout.togglebutton_checked, "togglebutton_checked", null),
-            new Layout(R.layout.togglebutton, "togglebutton", null),
-            new Layout(R.layout.zoomcontrols, "zoomcontrols", null),
-    };
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java b/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java
new file mode 100644
index 0000000..7569252
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.io.File;
+
+/**
+ * Activity test case used to instrument generation of reference images.
+ */
+public class ReferenceImagesTest extends ActivityInstrumentationTestCase2<GenerateImagesActivity> {
+
+    public ReferenceImagesTest() {
+        super(GenerateImagesActivity.class);
+    }
+
+    public void testGenerateReferenceImages() throws Exception {
+        setActivityInitialTouchMode(true);
+
+        final GenerateImagesActivity activity = getActivity();
+        activity.waitForCompletion();
+
+        assertTrue(activity.getFinishReason(), activity.isFinishSuccess());
+
+        final File outputDir = activity.getOutputDir();
+        final File outputZip = new File(outputDir.getParentFile(), outputDir.getName() + ".zip");
+        if (outputZip.exists()) {
+            // Remove any old test results.
+            outputZip.delete();
+        }
+
+        ThemeTestUtils.compressDirectory(outputDir, outputZip);
+        ThemeTestUtils.deleteDirectory(outputDir);
+
+        assertTrue("Generated reference image ZIP", outputZip.exists());
+    }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
new file mode 100644
index 0000000..d8b1f30
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.theme.app.modifiers.DatePickerModifier;
+import android.theme.app.modifiers.ProgressBarModifier;
+import android.theme.app.modifiers.SearchViewModifier;
+import android.theme.app.modifiers.TimePickerModifier;
+import android.theme.app.modifiers.ViewCheckedModifier;
+import android.theme.app.modifiers.ViewPressedModifier;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.DatePicker;
+
+import java.io.File;
+import java.lang.Override;
+
+/**
+ * A activity which display various UI elements with non-modifiable themes.
+ */
+public class ThemeDeviceActivity extends Activity {
+    public static final String EXTRA_THEME = "theme";
+    public static final String EXTRA_OUTPUT_DIR = "outputDir";
+
+    private static final String TAG = "ThemeDeviceActivity";
+
+    /**
+     * The duration of the CalendarView adjustment to settle to its final
+     * position.
+     */
+    private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
+
+    private Theme mTheme;
+    private ReferenceViewGroup mViewGroup;
+    private File mOutputDir;
+    private int mLayoutIndex;
+    private boolean mIsRunning;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Intent intent = getIntent();
+        final int themeIndex = intent.getIntExtra(EXTRA_THEME, -1);
+        if (themeIndex < 0) {
+            Log.e(TAG, "No theme specified");
+            finish();
+        }
+
+        final String outputDir = intent.getStringExtra(EXTRA_OUTPUT_DIR);
+        if (outputDir == null) {
+            Log.e(TAG, "No output directory specified");
+            finish();
+        }
+
+        mOutputDir = new File(outputDir);
+        mTheme = THEMES[themeIndex];
+
+        setTheme(mTheme.id);
+        setContentView(R.layout.theme_test);
+
+        mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
+
+        getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
+                | LayoutParams.FLAG_TURN_SCREEN_ON
+                | LayoutParams.FLAG_DISMISS_KEYGUARD );
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mIsRunning = true;
+
+        setNextLayout();
+    }
+
+    @Override
+    protected void onPause() {
+        mIsRunning = false;
+
+        if (!isFinishing()) {
+            // The activity paused for some reason, likely a system crash
+            // dialog. Finish it so we can move to the next theme.
+            Log.w(TAG, "onPause() called without a call to finish()", new RuntimeException());
+            finish();
+        }
+
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mLayoutIndex < LAYOUTS.length) {
+            Log.e(TAG, "Not all layouts got rendered: " + mLayoutIndex);
+            setResult(RESULT_CANCELED);
+        }
+
+        super.onDestroy();
+    }
+
+    /**
+     * Sets the next layout in the UI.
+     */
+    private void setNextLayout() {
+        if (mLayoutIndex >= LAYOUTS.length) {
+            setResult(RESULT_OK);
+            finish();
+            return;
+        }
+
+        mViewGroup.removeAllViews();
+
+        final Layout layout = LAYOUTS[mLayoutIndex++];
+        final String layoutName = String.format("%s_%s", mTheme.name, layout.name);
+        final View view = getLayoutInflater().inflate(layout.id, mViewGroup, false);
+        if (layout.modifier != null) {
+            layout.modifier.modifyView(view);
+        }
+
+        mViewGroup.addView(view);
+        view.setFocusable(false);
+
+        Log.v(TAG, "Rendering layout " + layoutName
+                + " (" + mLayoutIndex + "/" + LAYOUTS.length + ")");
+
+        final Runnable generateBitmapRunnable = new Runnable() {
+            @Override
+            public void run() {
+                new BitmapTask(view, layoutName).execute();
+            }
+        };
+
+        if (view instanceof DatePicker) {
+            // The Holo-styled DatePicker uses a CalendarView that has a
+            // non-configurable adjustment duration of 540ms.
+            view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
+        } else {
+            view.post(generateBitmapRunnable);
+        }
+    }
+
+    private class BitmapTask extends GenerateBitmapTask {
+        public BitmapTask(View view, String name) {
+            super(view, mOutputDir, name);
+        }
+
+        @Override
+        protected void onPostExecute(Boolean success) {
+            if (success && mIsRunning) {
+                setNextLayout();
+            } else {
+                Log.e(TAG, "Failed to render view to bitmap: " + mName + " (activity running? "
+                        + mIsRunning + ")");
+                finish();
+            }
+        }
+    }
+
+    /**
+     * A class to encapsulate information about a theme.
+     */
+    static class Theme {
+        public final int id;
+        public final int apiLevel;
+        public final String name;
+
+        private Theme(int id, int apiLevel, String name) {
+            this.id = id;
+            this.apiLevel = apiLevel;
+            this.name = name;
+        }
+    }
+
+    // List of themes to verify.
+    static final Theme[] THEMES = {
+            // Holo
+            new Theme(android.R.style.Theme_Holo,
+                    Build.VERSION_CODES.HONEYCOMB, "holo"),
+            new Theme(android.R.style.Theme_Holo_Dialog,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog"),
+            new Theme(android.R.style.Theme_Holo_Dialog_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"),
+            new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"),
+            new Theme(android.R.style.Theme_Holo_DialogWhenLarge,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"),
+            new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_InputMethod,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"),
+            new Theme(android.R.style.Theme_Holo_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"),
+            new Theme(android.R.style.Theme_Holo_NoActionBar_Overscan,
+                    Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"),
+            new Theme(android.R.style.Theme_Holo_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"),
+            new Theme(android.R.style.Theme_Holo_Panel,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_panel"),
+            new Theme(android.R.style.Theme_Holo_Wallpaper,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"),
+            new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"),
+
+            // Holo Light
+            new Theme(android.R.style.Theme_Holo_Light,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light"),
+            new Theme(android.R.style.Theme_Holo_Light_DarkActionBar,
+                    Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"),
+            new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"),
+            new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"),
+            new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Overscan,
+                    Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"),
+            new Theme(android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"),
+            new Theme(android.R.style.Theme_Holo_Light_Panel,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"),
+
+            // Material
+            new Theme(android.R.style.Theme_Material,
+                    Build.VERSION_CODES.LOLLIPOP, "material"),
+            new Theme(android.R.style.Theme_Material_Dialog,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog"),
+            new Theme(android.R.style.Theme_Material_Dialog_Alert,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"),
+            new Theme(android.R.style.Theme_Material_Dialog_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"),
+            new Theme(android.R.style.Theme_Material_Dialog_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"),
+            new Theme(android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"),
+            new Theme(android.R.style.Theme_Material_Dialog_Presentation,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"),
+            new Theme(android.R.style.Theme_Material_DialogWhenLarge,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"),
+            new Theme(android.R.style.Theme_Material_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"),
+            new Theme(android.R.style.Theme_Material_InputMethod,
+                    Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"),
+            new Theme(android.R.style.Theme_Material_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"),
+            new Theme(android.R.style.Theme_Material_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"),
+            new Theme(android.R.style.Theme_Material_NoActionBar_Overscan,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"),
+            new Theme(android.R.style.Theme_Material_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"),
+            new Theme(android.R.style.Theme_Material_Panel,
+                    Build.VERSION_CODES.LOLLIPOP, "material_panel"),
+            new Theme(android.R.style.Theme_Material_Settings,
+                    Build.VERSION_CODES.LOLLIPOP, "material_settings"),
+            new Theme(android.R.style.Theme_Material_Voice,
+                    Build.VERSION_CODES.LOLLIPOP, "material_voice"),
+            new Theme(android.R.style.Theme_Material_Wallpaper,
+                    Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"),
+            new Theme(android.R.style.Theme_Material_Wallpaper_NoTitleBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"),
+
+            // Material Light
+            new Theme(android.R.style.Theme_Material_Light,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light"),
+            new Theme(android.R.style.Theme_Material_Light_DarkActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"),
+            new Theme(android.R.style.Theme_Material_Light_Dialog,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"),
+            new Theme(android.R.style.Theme_Material_Light_Dialog_Alert,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"),
+            new Theme(android.R.style.Theme_Material_Light_Dialog_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"),
+            new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"),
+            new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"),
+            new Theme(android.R.style.Theme_Material_Light_Dialog_Presentation,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"),
+            new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"),
+            new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"),
+            new Theme(android.R.style.Theme_Material_Light_LightStatusBar,
+                    Build.VERSION_CODES.M, "material_light_lightstatusbar"),
+            new Theme(android.R.style.Theme_Material_Light_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"),
+            new Theme(android.R.style.Theme_Material_Light_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"),
+            new Theme(android.R.style.Theme_Material_Light_NoActionBar_Overscan,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"),
+            new Theme(android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"),
+            new Theme(android.R.style.Theme_Material_Light_Panel,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_panel"),
+            new Theme(android.R.style.Theme_Material_Light_Voice,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_voice")
+    };
+
+    /**
+     * A class to encapsulate information about a layout.
+     */
+    private static class Layout {
+        public final int id;
+        public final String name;
+        public final LayoutModifier modifier;
+
+        private Layout(int id, String name) {
+            this(id, name, null);
+        }
+
+        private Layout(int id, String name, LayoutModifier modifier) {
+            this.id = id;
+            this.name = name;
+            this.modifier = modifier;
+        }
+    }
+
+    // List of layouts to verify for each theme.
+    private static final Layout[] LAYOUTS = {
+            new Layout(R.layout.button, "button"),
+            new Layout(R.layout.button, "button_pressed",
+                    new ViewPressedModifier()),
+            new Layout(R.layout.checkbox, "checkbox"),
+            new Layout(R.layout.checkbox, "checkbox_checked",
+                    new ViewCheckedModifier()),
+            new Layout(R.layout.chronometer, "chronometer"),
+            new Layout(R.layout.color_blue_bright, "color_blue_bright"),
+            new Layout(R.layout.color_blue_dark, "color_blue_dark"),
+            new Layout(R.layout.color_blue_light, "color_blue_light"),
+            new Layout(R.layout.color_green_dark, "color_green_dark"),
+            new Layout(R.layout.color_green_light, "color_green_light"),
+            new Layout(R.layout.color_orange_dark, "color_orange_dark"),
+            new Layout(R.layout.color_orange_light, "color_orange_light"),
+            new Layout(R.layout.color_purple, "color_purple"),
+            new Layout(R.layout.color_red_dark, "color_red_dark"),
+            new Layout(R.layout.color_red_light, "color_red_light"),
+            new Layout(R.layout.datepicker, "datepicker",
+                    new DatePickerModifier()),
+            new Layout(R.layout.display_info, "display_info"),
+            new Layout(R.layout.edittext, "edittext"),
+            new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0"),
+            new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100"),
+            new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50"),
+            new Layout(R.layout.progressbar_large, "progressbar_large",
+                    new ProgressBarModifier()),
+            new Layout(R.layout.progressbar_small, "progressbar_small",
+                    new ProgressBarModifier()),
+            new Layout(R.layout.progressbar, "progressbar",
+                    new ProgressBarModifier()),
+            new Layout(R.layout.radiobutton_checked, "radiobutton_checked"),
+            new Layout(R.layout.radiobutton, "radiobutton"),
+            new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal"),
+            new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical"),
+            new Layout(R.layout.ratingbar_0, "ratingbar_0"),
+            new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5"),
+            new Layout(R.layout.ratingbar_5, "ratingbar_5"),
+            new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed",
+                    new ViewPressedModifier()),
+            new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed",
+                    new ViewPressedModifier()),
+            new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed",
+                    new ViewPressedModifier()),
+            new Layout(R.layout.searchview, "searchview_query",
+                    new SearchViewModifier(SearchViewModifier.QUERY)),
+            new Layout(R.layout.searchview, "searchview_query_hint",
+                    new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
+            new Layout(R.layout.seekbar_0, "seekbar_0"),
+            new Layout(R.layout.seekbar_100, "seekbar_100"),
+            new Layout(R.layout.seekbar_50, "seekbar_50"),
+            new Layout(R.layout.spinner, "spinner"),
+            new Layout(R.layout.switch_button_checked, "switch_button_checked"),
+            new Layout(R.layout.switch_button, "switch_button"),
+            new Layout(R.layout.textview, "textview"),
+            new Layout(R.layout.timepicker, "timepicker",
+                    new TimePickerModifier()),
+            new Layout(R.layout.togglebutton_checked, "togglebutton_checked"),
+            new Layout(R.layout.togglebutton, "togglebutton"),
+            new Layout(R.layout.zoomcontrols, "zoomcontrols"),
+    };
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java b/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java
new file mode 100644
index 0000000..4daca6c
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class ThemeTestUtils {
+
+    /**
+     * Compresses the contents of a directory to a ZIP file.
+     *
+     * @param dir the directory to compress
+     * @return {@code true} on success, {@code false} on failure
+     */
+    public static boolean compressDirectory(File dir, File file) throws IOException {
+        if (dir == null || !dir.exists() || file == null || file.exists()) {
+            return false;
+        }
+
+        final File[] srcFiles = dir.listFiles();
+        if (srcFiles.length == 0) {
+            return false;
+        }
+
+        final ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(file));
+        final byte[] data = new byte[4096];
+        for (int i = 0; i < srcFiles.length; i++) {
+            final FileInputStream fileIn = new FileInputStream(srcFiles[i]);
+            final ZipEntry entry = new ZipEntry(srcFiles[i].getName());
+            zipOut.putNextEntry(entry);
+
+            int count;
+            while ((count = fileIn.read(data, 0, data.length)) != -1) {
+                zipOut.write(data, 0, count);
+                zipOut.flush();
+            }
+
+            zipOut.closeEntry();
+            fileIn.close();
+        }
+
+        zipOut.close();
+        return true;
+    }
+
+    /**
+     * Recursively deletes a directory and its contents.
+     *
+     * @param dir the directory to delete
+     * @return {@code true} on success, {@code false} on failure
+     */
+    public static boolean deleteDirectory(File dir) {
+        final File files[] = dir.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                deleteDirectory(file);
+            }
+        }
+        return dir.delete();
+    }
+}
diff --git a/hostsidetests/theme/assets/23/400dpi.zip b/hostsidetests/theme/assets/23/400dpi.zip
new file mode 100644
index 0000000..be0891f
--- /dev/null
+++ b/hostsidetests/theme/assets/23/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/560dpi.zip b/hostsidetests/theme/assets/23/560dpi.zip
new file mode 100644
index 0000000..cf0a559
--- /dev/null
+++ b/hostsidetests/theme/assets/23/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/hdpi.zip b/hostsidetests/theme/assets/23/hdpi.zip
new file mode 100644
index 0000000..80c12a7
--- /dev/null
+++ b/hostsidetests/theme/assets/23/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/ldpi.zip b/hostsidetests/theme/assets/23/ldpi.zip
new file mode 100644
index 0000000..937914a
--- /dev/null
+++ b/hostsidetests/theme/assets/23/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/mdpi.zip b/hostsidetests/theme/assets/23/mdpi.zip
new file mode 100644
index 0000000..f842676
--- /dev/null
+++ b/hostsidetests/theme/assets/23/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/tvdpi.zip b/hostsidetests/theme/assets/23/tvdpi.zip
new file mode 100644
index 0000000..77386e5
--- /dev/null
+++ b/hostsidetests/theme/assets/23/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/xhdpi.zip b/hostsidetests/theme/assets/23/xhdpi.zip
new file mode 100644
index 0000000..a8310d5
--- /dev/null
+++ b/hostsidetests/theme/assets/23/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/xxhdpi.zip b/hostsidetests/theme/assets/23/xxhdpi.zip
new file mode 100644
index 0000000..f88711f
--- /dev/null
+++ b/hostsidetests/theme/assets/23/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/generate_images.sh b/hostsidetests/theme/generate_images.sh
new file mode 100755
index 0000000..9bcc3e5
--- /dev/null
+++ b/hostsidetests/theme/generate_images.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# 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.
+
+# This script is used to generate reference images for the CTS theme tests.
+# See the accompanying README file for more information.
+
+# retry <command> <tries> <message> <delay>
+function retry {
+  RETRY="0"
+  while true; do
+    if (("$RETRY" >= "$2")); then
+      echo $OUTPUT
+      exit
+    fi
+
+    OUTPUT=`$1 |& grep error`
+
+    if [ -z "$OUTPUT" ]; then
+      break
+    fi
+
+    echo $3
+    sleep $4
+    RETRY=$[$RETRY + 1]
+  done
+}
+
+themeApkPath="$ANDROID_HOST_OUT/cts/android-cts/repository/testcases/CtsThemeDeviceApp.apk"
+outDir="$ANDROID_BUILD_TOP/cts/hostsidetests/theme/assets"
+exe="$ANDROID_BUILD_TOP/cts/hostsidetests/theme/run_theme_capture_device.py"
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+  echo "Missing environment variables. Did you run build/envsetup.sh and lunch?"
+  exit
+fi
+
+if [ ! -e "$themeApkPath" ]; then
+  echo "Couldn't find test APK. Did you run make cts?"
+  exit
+fi
+
+adb devices
+python $exe $themeApkPath $outDir
diff --git a/hostsidetests/theme/run_theme_capture_device.py b/hostsidetests/theme/run_theme_capture_device.py
new file mode 100755
index 0000000..23171db
--- /dev/null
+++ b/hostsidetests/theme/run_theme_capture_device.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import os
+import sys
+import threading
+import time
+import Queue
+sys.path.append(sys.path[0])
+from android_device import *
+
+CTS_THEME_dict = {
+    120 : "ldpi",
+    160 : "mdpi",
+    213 : "tvdpi",
+    240 : "hdpi",
+    320 : "xhdpi",
+    400 : "400dpi",
+    480 : "xxhdpi",
+    560 : "560dpi",
+    640 : "xxxhdpi",
+}
+
+OUT_FILE = "/sdcard/cts-theme-assets.zip"
+
+# pass a function with number of instances to be executed in parallel
+# each thread continues until config q is empty.
+def executeParallel(tasks, setup, q, numberThreads):
+    class ParallelExecutor(threading.Thread):
+        def __init__(self, tasks, q):
+            threading.Thread.__init__(self)
+            self._q = q
+            self._tasks = tasks
+            self._setup = setup
+            self._result = 0
+
+        def run(self):
+            try:
+                while True:
+                    config = q.get(block=True, timeout=2)
+                    for t in self._tasks:
+                        try:
+                            if t(self._setup, config):
+                                self._result += 1
+                        except KeyboardInterrupt:
+                            raise
+                        except:
+                            print "Failed to execute thread:", sys.exc_info()[0]
+                    q.task_done()
+            except KeyboardInterrupt:
+                raise
+            except Queue.Empty:
+                pass
+
+        def getResult(self):
+            return self._result
+
+    result = 0;
+    threads = []
+    for i in range(numberThreads):
+        t = ParallelExecutor(tasks, q)
+        t.start()
+        threads.append(t)
+    for t in threads:
+        t.join()
+        result += t.getResult()
+    return result;
+
+def printAdbResult(device, out, err):
+    print "device: " + device
+    if out is not None:
+        print "out:\n" + out
+    if err is not None:
+        print "err:\n" + err
+
+def getResDir(outPath, resName):
+    resDir = outPath + "/" + resName
+    return resDir
+
+def doCapturing(setup, deviceSerial):
+    (themeApkPath, outPath) = setup
+
+    print "Found device: " + deviceSerial
+    device = androidDevice(deviceSerial)
+
+    # outPath = outPath + "/%d/" % (device.getSdkLevel()) + deviceSerial
+    outPath = outPath + "/%d" % (device.getSdkLevel())
+    density = device.getDensity()
+    resName = CTS_THEME_dict[density]
+
+    device.uninstallApk("android.theme.app")
+
+    (out, err, success) = device.installApk(themeApkPath)
+    if not success:
+        print "Failed to install APK on " + deviceSerial
+        printAdbResult(device, out, err)
+        return False
+
+    print "Generating images on " + deviceSerial + "..."
+    try:
+        (out, err) = device.runInstrumentationTest("android.theme.app/android.support.test.runner.AndroidJUnitRunner")
+    except KeyboardInterrupt:
+        raise
+    except:
+        (out, err) = device.runInstrumentationTest("android.theme.app/android.test.InstrumentationTestRunner")
+
+    # Detect test failure and abort.
+    if "FAILURES!!!" in out.split():
+        printAdbResult(deviceSerial, out, err)
+        return False
+
+    # Make sure that the run is complete by checking the process itself
+    print "Waiting for " + deviceSerial + "..."
+    waitTime = 0
+    while device.isProcessAlive("android.theme.app"):
+        time.sleep(1)
+        waitTime = waitTime + 1
+        if waitTime > 180:
+            print "Timed out"
+            break
+
+    time.sleep(10)
+    resDir = getResDir(outPath, resName)
+
+    print "Pulling images from " + deviceSerial + " to " + resDir + ".zip"
+    device.runAdbCommand("pull " + OUT_FILE + " " + resDir + ".zip")
+    device.runAdbCommand("shell rm -rf " + OUT_FILE)
+    return True
+
+def main(argv):
+    if len(argv) < 3:
+        print "run_theme_capture_device.py themeApkPath outDir"
+        sys.exit(1)
+    themeApkPath = argv[1]
+    outPath = os.path.abspath(argv[2])
+    os.system("mkdir -p " + outPath)
+
+    tasks = []
+    tasks.append(doCapturing)
+
+    devices = runAdbDevices();
+    numberThreads = len(devices)
+
+    configQ = Queue.Queue()
+    for device in devices:
+        configQ.put(device)
+    setup = (themeApkPath, outPath)
+    result = executeParallel(tasks, setup, configQ, numberThreads)
+
+    if result > 0:
+        print 'Generated reference images for %(count)d devices' % {"count": result}
+    else:
+        print 'Failed to generate reference images'
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
old mode 100644
new mode 100755
index ba880d7..0e11111
--- a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
+++ b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
@@ -23,6 +23,7 @@
 import java.awt.Color;
 import java.awt.image.BufferedImage;
 import java.io.File;
+import java.io.IOException;
 import java.lang.String;
 import java.util.concurrent.Callable;
 
@@ -32,59 +33,67 @@
  * Compares the images generated by the device with the reference images.
  */
 public class ComparisonTask implements Callable<Boolean> {
-
-    private static final String TAG = ComparisonTask.class.getSimpleName();
+    private static final String TAG = "ComparisonTask";
 
     private static final int IMAGE_THRESHOLD = 2;
 
-    private static final String STORAGE_PATH_DEVICE = "/sdcard/cts-holo-assets/%s.png";
-
     private final ITestDevice mDevice;
+    private final File mExpected;
+    private final File mActual;
 
-    private final File mReference;
-
-    private final String mName;
-
-    public ComparisonTask(ITestDevice device, File reference, String name) {
+    public ComparisonTask(ITestDevice device, File expected, File actual) {
         mDevice = device;
-        mReference = reference;
-        mName = name;
+        mExpected = expected;
+        mActual = actual;
     }
 
     public Boolean call() {
         boolean success = false;
-        File generated = null;
+
         try {
-            generated = File.createTempFile("gen_" + mName, ".png");
-
-            final String remoteGenerated = String.format(STORAGE_PATH_DEVICE, mName);
-            if (!mDevice.doesFileExist(remoteGenerated)) {
-                Log.logAndDisplay(LogLevel.ERROR, TAG, "File " + remoteGenerated + " have not been saved on device");
-                return false;
-            }
-            mDevice.pullFile(remoteGenerated, generated);
-
-            final BufferedImage ref = ImageIO.read(mReference);
-            final BufferedImage gen = ImageIO.read(generated);
-            if (compare(ref, gen, IMAGE_THRESHOLD)) {
+            final BufferedImage expected = ImageIO.read(mExpected);
+            final BufferedImage actual = ImageIO.read(mActual);
+            if (compare(expected, actual, IMAGE_THRESHOLD)) {
                 success = true;
             } else {
-                File diff = File.createTempFile("diff_" + mName, ".png");
-                createDiff(ref, gen, diff);
+                final File diff = File.createTempFile("diff_" + mExpected.getName(), ".png");
+                createDiff(expected, actual, diff);
                 Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath());
             }
-        } catch (Exception e) {
-            Log.logAndDisplay(LogLevel.ERROR, TAG, String.format(STORAGE_PATH_DEVICE, mName));
+        } catch (IOException e) {
             Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
             e.printStackTrace();
-        } finally {
-            if (generated != null) {
-                generated.delete();
-            }
         }
+
         return success;
     }
 
+    /**
+     * Verifies that the pixels of reference and generated images are similar
+     * within a specified threshold.
+     *
+     * @param expected expected image
+     * @param actual actual image
+     * @param threshold maximum difference per channel
+     * @return {@code true} if the images are similar, false otherwise
+     */
+    private static int getAlphaScaledBlue(final int color) {
+        return (color & 0x000000FF) * getAlpha(color) / 255;
+    }
+
+    private static int getAlphaScaledGreen(final int color) {
+        return ((color & 0x0000FF00) >> 8) * getAlpha(color) / 255;
+    }
+
+    private static int getAlphaScaledRed(final int color) {
+        return ((color & 0x00FF0000) >> 16) * getAlpha(color) / 255;
+    }
+
+    private static int getAlpha(final int color) {
+        // use logical shift for keeping an unsigned value
+        return (color & 0xFF000000) >>> 24;
+    }
+
     private static boolean compare(BufferedImage reference, BufferedImage generated, int threshold) {
         final int w = generated.getWidth();
         final int h = generated.getHeight();
@@ -96,15 +105,14 @@
             for (int j = 0; j < h; j++) {
                 final int p1 = reference.getRGB(i, j);
                 final int p2 = generated.getRGB(i, j);
-                final int dr = (p1 & 0x000000FF) - (p2 & 0x000000FF);
-                final int dg = ((p1 & 0x0000FF00) - (p2 & 0x0000FF00)) >> 8;
-                final int db = ((p1 & 0x00FF0000) - (p2 & 0x00FF0000)) >> 16;
-                final int da = ((p1 & 0xFF000000) - (p2 & 0xFF000000)) >> 24;
+
+                final int dr = getAlphaScaledRed(p1) - getAlphaScaledRed(p2);
+                final int dg = getAlphaScaledGreen(p1) - getAlphaScaledGreen(p2);
+                final int db = getAlphaScaledBlue(p1) - getAlphaScaledBlue(p2);
 
                 if (Math.abs(db) > threshold ||
                         Math.abs(dg) > threshold ||
-                        Math.abs(dr) > threshold ||
-                        Math.abs(da) > threshold) {
+                        Math.abs(dr) > threshold) {
                     return false;
                 }
             }
@@ -112,45 +120,49 @@
         return true;
     }
 
-    private static void createDiff(BufferedImage image1, BufferedImage image2, File out)
-            throws Exception {
-        final int w1 = image1.getWidth();
-        final int h1 = image1.getHeight();
-        final int w2 = image2.getWidth();
-        final int h2 = image2.getHeight();
+    private static void createDiff(BufferedImage expected, BufferedImage actual, File out)
+            throws IOException {
+        final int w1 = expected.getWidth();
+        final int h1 = expected.getHeight();
+        final int w2 = actual.getWidth();
+        final int h2 = actual.getHeight();
         final int width = Math.max(w1, w2);
         final int height = Math.max(h1, h2);
+
         // The diff will contain image1, image2 and the difference between the two.
-        final BufferedImage diff = new BufferedImage(width * 3, height, BufferedImage.TYPE_INT_ARGB);
+        final BufferedImage diff = new BufferedImage(
+                width * 3, height, BufferedImage.TYPE_INT_ARGB);
 
         for (int i = 0; i < width; i++) {
             for (int j = 0; j < height; j++) {
                 final boolean inBounds1 = i < w1 && j < h1;
                 final boolean inBounds2 = i < w2 && j < h2;
-                int color1 = Color.WHITE.getRGB();
-                int color2 = Color.WHITE.getRGB();
-                int color3;
+                int colorExpected = Color.WHITE.getRGB();
+                int colorActual = Color.WHITE.getRGB();
+                int colorDiff;
                 if (inBounds1 && inBounds2) {
-                    color1 = image1.getRGB(i, j);
-                    color2 = image2.getRGB(i, j);
-                    color3 = color1 == color2 ? color1 : Color.RED.getRGB();
+                    colorExpected = expected.getRGB(i, j);
+                    colorActual = actual.getRGB(i, j);
+                    colorDiff = colorExpected == colorActual ? colorExpected : Color.RED.getRGB();
                 } else if (inBounds1 && !inBounds2) {
-                    color1 = image1.getRGB(i, j);
-                    color3 = Color.BLUE.getRGB();
+                    colorExpected = expected.getRGB(i, j);
+                    colorDiff = Color.BLUE.getRGB();
                 } else if (!inBounds1 && inBounds2) {
-                    color2 = image2.getRGB(i, j);
-                    color3 = Color.GREEN.getRGB();
+                    colorActual = actual.getRGB(i, j);
+                    colorDiff = Color.GREEN.getRGB();
                 } else {
-                    color3 = Color.MAGENTA.getRGB();
+                    colorDiff = Color.MAGENTA.getRGB();
                 }
+
                 int x = i;
-                diff.setRGB(x, j, color1);
+                diff.setRGB(x, j, colorExpected);
                 x += width;
-                diff.setRGB(x, j, color2);
+                diff.setRGB(x, j, colorActual);
                 x += width;
-                diff.setRGB(x, j, color3);
+                diff.setRGB(x, j, colorDiff);
             }
         }
+
         ImageIO.write(diff, "png", out);
     }
 
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index 8326b1f..b4bb748 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -21,8 +21,8 @@
 import com.android.cts.util.TimeoutReq;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
-import com.android.ddmlib.IShellOutputReceiver;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IAbi;
@@ -30,145 +30,44 @@
 import com.android.tradefed.testtype.IBuildReceiver;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.String;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Scanner;
-import java.util.concurrent.Callable;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorCompletionService;
 import java.util.concurrent.ExecutorService;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
 /**
- * Test to check the Holo theme has not been changed.
+ * Test to check non-modifiable themes have not been changed.
  */
 public class ThemeHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+    private static final String LOG_TAG = "ThemeHostTest";
+    private static final String APK_NAME = "CtsThemeDeviceApp";
+    private static final String APP_PACKAGE_NAME = "android.theme.app";
 
-    private static final String TAG = ThemeHostTest.class.getSimpleName();
-
-    private static final int ADB_TIMEOUT = 60 * 60 * 1000;//60mins in ms
-
-    /** The package name of the APK. */
-    private static final String PACKAGE = "android.theme.app";
-
-    /** The file name of the APK. */
-    private static final String APK = "CtsThemeDeviceApp.apk";
+    private static final String GENERATED_ASSETS_ZIP = "/sdcard/cts-theme-assets.zip";
 
     /** The class name of the main activity in the APK. */
-    private static final String CLASS = "HoloDeviceActivity";
+    private static final String CLASS = "GenerateImagesActivity";
 
     /** The command to launch the main activity. */
     private static final String START_CMD = String.format(
-            "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+            "am start -W -a android.intent.action.MAIN -n %s/%s.%s", APP_PACKAGE_NAME,
+            APP_PACKAGE_NAME, CLASS);
 
-    private static final String CLEAR_GENERATED_CMD = "rm -rf /sdcard/cts-holo-assets/*.png";
-
-    private static final String STOP_CMD = String.format("am force-stop %s", PACKAGE);
-
+    private static final String CLEAR_GENERATED_CMD = "rm -rf %s/*.png";
+    private static final String STOP_CMD = String.format("am force-stop %s", APP_PACKAGE_NAME);
     private static final String HARDWARE_TYPE_CMD = "dumpsys | grep android.hardware.type";
-
     private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density";
-
     private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density";
 
-    // Intent extras
-    protected final static String INTENT_STRING_EXTRA = " --es %s %s";
-
-    protected final static String INTENT_BOOLEAN_EXTRA = " --ez %s %b";
-
-    protected final static String INTENT_INTEGER_EXTRA = " --ei %s %d";
-
-    // Intent extra keys
-    private static final String EXTRA_THEME = "holo_theme_extra";
-
-    private static final String[] THEMES = {
-            "holo",
-            "holo_dialog",
-            "holo_dialog_minwidth",
-            "holo_dialog_noactionbar",
-            "holo_dialog_noactionbar_minwidth",
-            "holo_dialogwhenlarge",
-            "holo_dialogwhenlarge_noactionbar",
-            "holo_inputmethod",
-            "holo_light",
-            "holo_light_darkactionbar",
-            "holo_light_dialog",
-            "holo_light_dialog_minwidth",
-            "holo_light_dialog_noactionbar",
-            "holo_light_dialog_noactionbar_minwidth",
-            "holo_light_dialogwhenlarge",
-            "holo_light_dialogwhenlarge_noactionbar",
-            "holo_light_noactionbar",
-            "holo_light_noactionbar_fullscreen",
-            "holo_light_panel",
-            "holo_noactionbar",
-            "holo_noactionbar_fullscreen",
-            "holo_panel",
-            "holo_wallpaper",
-            "holo_wallpaper_notitlebar",
-    };
-
-    private final int NUM_THEMES = THEMES.length;
-
-    private static final String[] LAYOUTS = {
-            "button",
-            "button_pressed",
-            "checkbox",
-            "checkbox_checked",
-            "chronometer",
-            "color_blue_bright",
-            "color_blue_dark",
-            "color_blue_light",
-            "color_green_dark",
-            "color_green_light",
-            "color_orange_dark",
-            "color_orange_light",
-            "color_purple",
-            "color_red_dark",
-            "color_red_light",
-            "datepicker",
-            "display_info",
-            "edittext",
-            "progressbar_horizontal_0",
-            "progressbar_horizontal_100",
-            "progressbar_horizontal_50",
-            "progressbar_large",
-            "progressbar_small",
-            "progressbar",
-            "radiobutton_checked",
-            "radiobutton",
-            "radiogroup_horizontal",
-            "radiogroup_vertical",
-            "ratingbar_0",
-            "ratingbar_2point5",
-            "ratingbar_5",
-            "ratingbar_0_pressed",
-            "ratingbar_2point5_pressed",
-            "ratingbar_5_pressed",
-            "searchview_query",
-            "searchview_query_hint",
-            "seekbar_0",
-            "seekbar_100",
-            "seekbar_50",
-            "spinner",
-            "switch_button_checked",
-            "switch_button",
-            "textview",
-            "timepicker",
-            "togglebutton_checked",
-            "togglebutton",
-            "zoomcontrols",
-    };
-
-    private final int NUM_LAYOUTS = LAYOUTS.length;
-
-    private final HashMap<String, File> mReferences = new HashMap<String, File>();
+    private final HashMap<String, File> mReferences = new HashMap<>();
 
     /** The ABI to use. */
     private IAbi mAbi;
@@ -197,32 +96,21 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        // Get the device, this gives a handle to run commands and install APKs.
+
         mDevice = getDevice();
-        // Remove any previously installed versions of this APK.
-        mDevice.uninstallPackage(PACKAGE);
+        mDevice.uninstallPackage(APP_PACKAGE_NAME);
+
         // Get the APK from the build.
-        File app = mBuild.getTestApp(APK);
-        // Get the ABI flag.
-        String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
-        // Install the APK on the device.
+        final File app = mBuild.getTestApp(String.format("%s.apk", APK_NAME));
+        final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+
         mDevice.installPackage(app, false, options);
-        // Remove previously generated images.
-        mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
-        final String densityProp;
 
-        if (mDevice.getSerialNumber().startsWith("emulator-")) {
-            densityProp = DENSITY_PROP_EMULATOR;
-        } else {
-            densityProp = DENSITY_PROP_DEVICE;
-        }
+        final String density = getDensityBucketForDevice(mDevice);
+        final String zipFile = String.format("/%s.zip", density);
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Loading resources from " + zipFile);
 
-        final String zip = String.format("/%s.zip",
-                getDensityBucket(Integer.parseInt(mDevice.getProperty(densityProp))));
-        Log.logAndDisplay(LogLevel.INFO, TAG, "Loading resources from " + zip);
-
-
-        final InputStream zipStream = this.getClass().getResourceAsStream(zip);
+        final InputStream zipStream = ThemeHostTest.class.getResourceAsStream(zipFile);
         if (zipStream != null) {
             final ZipInputStream in = new ZipInputStream(zipStream);
             try {
@@ -232,21 +120,28 @@
                     final String name = ze.getName();
                     final File tmp = File.createTempFile("ref_" + name, ".png");
                     final FileOutputStream out = new FileOutputStream(tmp);
+
                     int count;
                     while ((count = in.read(buffer)) != -1) {
                         out.write(buffer, 0, count);
                     }
+
                     out.flush();
                     out.close();
                     mReferences.put(name, tmp);
                 }
+            } catch (IOException e) {
+                Log.logAndDisplay(LogLevel.ERROR, LOG_TAG, "Failed to unzip assets: " + zipFile);
             } finally {
                 in.close();
             }
+        } else {
+            Log.logAndDisplay(LogLevel.ERROR, LOG_TAG, "Failed to get resource: " + zipFile);
         }
 
-        mExecutionService = Executors.newFixedThreadPool(2);// 2 worker threads
-        mCompletionService = new ExecutorCompletionService<Boolean>(mExecutionService);
+        final int numCores = Runtime.getRuntime().availableProcessors();
+        mExecutionService = Executors.newFixedThreadPool(numCores * 2);
+        mCompletionService = new ExecutorCompletionService<>(mExecutionService);
     }
 
     @Override
@@ -255,86 +150,148 @@
         for (File ref : mReferences.values()) {
             ref.delete();
         }
+
         mExecutionService.shutdown();
+
         // Remove the APK.
-        mDevice.uninstallPackage(PACKAGE);
+        mDevice.uninstallPackage(APP_PACKAGE_NAME);
+
         // Remove generated images.
         mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
+
         super.tearDown();
     }
 
     @TimeoutReq(minutes = 60)
-    public void testHoloThemes() throws Exception {
-        if (checkHardwareTypeSkipTest(
-                mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
-            Log.logAndDisplay(LogLevel.INFO, TAG, "Skipped HoloThemes test for watch and TV");
+    public void testThemes() throws Exception {
+        if (checkHardwareTypeSkipTest(mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
+            Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test for watch");
             return;
         }
 
         if (mReferences.isEmpty()) {
-            Log.logAndDisplay(LogLevel.INFO, TAG,
-                    "Skipped HoloThemes test due to no reference images");
+            Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test due to no reference images");
             return;
         }
 
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Generating device images...");
+
+        assertTrue("Aborted image generation", generateDeviceImages());
+
+        // Pull ZIP file from remote device.
+        final File localZip = File.createTempFile("generated", ".zip");
+        mDevice.pullFile(GENERATED_ASSETS_ZIP, localZip);
+
         int numTasks = 0;
-        for (int i = 0; i < NUM_THEMES; i++) {
-            final String themeName = THEMES[i];
-            runCapture(i, themeName);
-            for (int j = 0; j < NUM_LAYOUTS; j++) {
-                final String name = String.format("%s_%s", themeName, LAYOUTS[j]);
-                final File ref = mReferences.get(name + ".png");
-                if (!ref.exists()) {
-                    Log.logAndDisplay(LogLevel.INFO, TAG,
-                            "Skipping theme test due to missing reference for reference image " +
-                            name);
-                    continue;
+
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Extracting generated images...");
+
+        // Extract generated images to temporary files.
+        final byte[] data = new byte[4096];
+        final ZipInputStream zipInput = new ZipInputStream(new FileInputStream(localZip));
+        ZipEntry entry;
+        while ((entry = zipInput.getNextEntry()) != null) {
+            final String name = entry.getName();
+            final File expected = mReferences.get(name);
+            if (expected != null && expected.exists()) {
+                final File actual = File.createTempFile("actual_" + name, ".png");
+                final FileOutputStream pngOutput = new FileOutputStream(actual);
+
+                int count;
+                while ((count = zipInput.read(data, 0, data.length)) != -1) {
+                    pngOutput.write(data, 0, count);
                 }
-                mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
+
+                pngOutput.flush();
+                pngOutput.close();
+
+                mCompletionService.submit(new ComparisonTask(mDevice, expected, actual));
                 numTasks++;
+            } else {
+                Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Missing reference image for " + name);
             }
+
+            zipInput.closeEntry();
         }
+
+        zipInput.close();
+
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Waiting for comparison tasks...");
+
         int failures = 0;
-        for (int i = 0; i < numTasks; i++) {
+        for (int i = numTasks; i > 0; i--) {
             failures += mCompletionService.take().get() ? 0 : 1;
         }
+
         assertTrue(failures + " failures in theme test", failures == 0);
+
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Finished!");
     }
 
-    private void runCapture(int themeId, String themeName) throws Exception {
-        final StringBuilder sb = new StringBuilder(START_CMD);
-        sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_THEME, themeId));
-        final String startCommand = sb.toString();
+    private boolean generateDeviceImages() throws Exception {
         // Clear logcat
         mDevice.executeAdbCommand("logcat", "-c");
+
         // Stop any existing instances
         mDevice.executeShellCommand(STOP_CMD);
-        // Start activity
-        mDevice.executeShellCommand(startCommand);
 
+        // Start activity
+        mDevice.executeShellCommand(START_CMD);
+
+        Log.logAndDisplay(LogLevel.VERBOSE, LOG_TAG, "Starting image generation...");
+
+        boolean aborted = false;
         boolean waiting = true;
         do {
             // Dump logcat.
             final String logs = mDevice.executeAdbCommand(
                     "logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
+
             // Search for string.
             final Scanner in = new Scanner(logs);
             while (in.hasNextLine()) {
                 final String line = in.nextLine();
                 if (line.startsWith("I/" + CLASS)) {
                     final String[] lineSplit = line.split(":");
-                    final String s = lineSplit[1].trim();
-                    final String themeNameGenerated = lineSplit[2].trim();
-                    if (s.equals("OKAY") && themeNameGenerated.equals(themeName)) {
-                        waiting = false;
+                    if (lineSplit.length >= 3) {
+                        final String cmd = lineSplit[1].trim();
+                        final String arg = lineSplit[2].trim();
+                        switch (cmd) {
+                            case "FAIL":
+                                Log.logAndDisplay(LogLevel.WARN, LOG_TAG, line);
+                                Log.logAndDisplay(LogLevel.WARN, LOG_TAG, "Aborting! Check host logs for details.");
+                                aborted = true;
+                                // fall-through
+                            case "OKAY":
+                                waiting = false;
+                                break;
+                        }
                     }
                 }
             }
             in.close();
-        } while (waiting);
+        } while (waiting && !aborted);
+
+        Log.logAndDisplay(LogLevel.VERBOSE, LOG_TAG, "Image generation completed!");
+
+        return !aborted;
     }
 
-    private static String getDensityBucket(int density) {
+    private static String getDensityBucketForDevice(ITestDevice device) {
+        final String densityProp;
+        if (device.getSerialNumber().startsWith("emulator-")) {
+            densityProp = DENSITY_PROP_EMULATOR;
+        } else {
+            densityProp = DENSITY_PROP_DEVICE;
+        }
+
+        final int density;
+        try {
+            density = Integer.parseInt(device.getProperty(densityProp));
+        } catch (DeviceNotAvailableException e) {
+            return "unknown";
+        }
+
         switch (density) {
             case 120:
                 return "ldpi";
@@ -363,9 +320,7 @@
         if (hardwareTypeString.contains("android.hardware.type.watch")) {
             return true;
         }
-        if (hardwareTypeString.contains("android.hardware.type.television")) {
-            return true;
-        }
+
         return false;
     }
 }
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
index 7e2bfe3..b3ebbad 100755
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -330,13 +330,34 @@
                 ex.release();
             }
         }
-        return false;
+        return true;
     }
 
     public static boolean checkCodecsForPath(Context context, String path) {
         return check(hasCodecsForPath(context, path), "no decoder found");
     }
 
+    public static boolean hasCodecForDomain(boolean encoder, String domain) {
+        for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+            if (encoder != info.isEncoder()) {
+                continue;
+            }
+
+            for (String type : info.getSupportedTypes()) {
+                if (type.toLowerCase().startsWith(domain.toLowerCase() + "/")) {
+                    Log.i(TAG, "found codec " + info.getName() + " for mime " + type);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public static boolean checkCodecForDomain(boolean encoder, String domain) {
+        return check(hasCodecForDomain(encoder, domain),
+                "no " + domain + (encoder ? " encoder" : " decoder") + " found");
+    }
+
     private static boolean hasCodecForMime(boolean encoder, String mime) {
         for (MediaCodecInfo info : sMCL.getCodecInfos()) {
             if (encoder != info.isEncoder()) {
diff --git a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp
index b8e2acf..16504fd 100644
--- a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp
@@ -22,6 +22,14 @@
 
 // Used to center the grid on the screen.
 #define CENTER_GRID(x) ((((x) * 2.0 + 1) - OFFSCREEN_GRID_SIZE) / OFFSCREEN_GRID_SIZE)
+// Leave a good error message if something fails.
+#define EGL_RESULT_CHECK(X) do { \
+                                   EGLint error = eglGetError(); \
+                                   if (!(X) || error != EGL_SUCCESS) { \
+                                       ALOGE("EGL error '%d' at %s:%d", error, __FILE__, __LINE__);\
+                                       return false; \
+                                    } \
+                            } while (0)
 
 static const int FBO_NUM_VERTICES = 6;
 
@@ -66,7 +74,7 @@
         EGL_NONE };
 
 static const EGLint configAttribs[] = {
-        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
         EGL_RED_SIZE, 8,
         EGL_GREEN_SIZE, 8,
@@ -78,53 +86,60 @@
 
 static const int FBO_SIZE = 128;
 
-Renderer::Renderer(ANativeWindow* window, bool offscreen, int workload) :
-        mOffscreen(offscreen), mWindow(window), mEglDisplay(EGL_NO_DISPLAY),
-        mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), mWorkload(workload) {
+Renderer::Renderer(EGLNativeWindowType window, bool offscreen) :
+        mOffscreen(offscreen), mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE),
+        mEglContext(EGL_NO_CONTEXT), mWindow(window)  {
 }
 
-bool Renderer::setUp() {
+bool Renderer::eglSetUp() {
     SCOPED_TRACE();
     mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (EGL_NO_DISPLAY == mEglDisplay || EGL_SUCCESS != eglGetError()) {
-        return false;
-    }
+    EGL_RESULT_CHECK(mEglDisplay != EGL_NO_DISPLAY);
 
     EGLint major;
     EGLint minor;
-    if (!eglInitialize(mEglDisplay, &major, &minor) || EGL_SUCCESS != eglGetError()) {
-        return false;
-    }
+    EGL_RESULT_CHECK(eglInitialize(mEglDisplay, &major, &minor));
 
     EGLint numConfigs = 0;
-    if (!eglChooseConfig(mEglDisplay, configAttribs, &mGlConfig, 1, &numConfigs)
-            || EGL_SUCCESS != eglGetError()) {
-        return false;
-    }
+    EGL_RESULT_CHECK(eglChooseConfig(mEglDisplay, configAttribs, &mGlConfig, 1, &numConfigs)
+                     && (numConfigs > 0));
 
     mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, mWindow, NULL);
-    if (EGL_NO_SURFACE == mEglSurface || EGL_SUCCESS != eglGetError()) {
-        return false;
-    }
+    EGL_RESULT_CHECK(mEglSurface != EGL_NO_SURFACE);
 
     mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, contextAttribs);
-    if (EGL_NO_CONTEXT == mEglContext || EGL_SUCCESS != eglGetError()) {
-        return false;
+    EGL_RESULT_CHECK(mEglContext != EGL_NO_CONTEXT);
+
+    EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
+    EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &mWidth));
+    EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &mHeight));
+
+    return true;
+}
+
+void Renderer::eglTearDown() {
+    SCOPED_TRACE();
+    eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+    if (mEglContext != EGL_NO_CONTEXT) {
+        eglDestroyContext(mEglDisplay, mEglContext);
+        mEglContext = EGL_NO_CONTEXT;
     }
 
-    if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
-            || EGL_SUCCESS != eglGetError()) {
-        return false;
+    if (mEglSurface != EGL_NO_SURFACE) {
+        mEglSurface = EGL_NO_SURFACE;
     }
 
-    if (!eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &mWidth)
-            || EGL_SUCCESS != eglGetError()) {
-        return false;
+    if (mEglDisplay != EGL_NO_DISPLAY) {
+        eglTerminate(mEglDisplay);
+        mEglDisplay = EGL_NO_DISPLAY;
     }
-    if (!eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &mHeight)
-            || EGL_SUCCESS != eglGetError()) {
-        return false;
-    }
+}
+
+bool Renderer::setUp(int /*workload*/) {
+    SCOPED_TRACE();
+
+    EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
 
     if (mOffscreen) {
         mFboWidth = FBO_SIZE;
@@ -178,6 +193,7 @@
         ALOGE("GLError %d in setUp", err);
         return false;
     }
+
     return true;
 }
 
@@ -202,29 +218,14 @@
         ALOGE("GLError %d in tearDown", err);
         return false;
     }
-    if (mEglContext != EGL_NO_CONTEXT) {
-        eglDestroyContext(mEglDisplay, mEglContext);
-        mEglContext = EGL_NO_CONTEXT;
-    }
-    if (mEglSurface != EGL_NO_SURFACE) {
-        eglDestroySurface(mEglDisplay, mEglSurface);
-        mEglSurface = EGL_NO_SURFACE;
-    }
-    if (mEglDisplay != EGL_NO_DISPLAY) {
-        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-        eglTerminate(mEglDisplay);
-        mEglDisplay = EGL_NO_DISPLAY;
-    }
 
-    return EGL_SUCCESS == eglGetError();
+    return true;
 }
 
 bool Renderer::draw() {
     SCOPED_TRACE();
-    if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
-            || EGL_SUCCESS != eglGetError()) {
-        return false;
-    }
+
+    EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
 
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
     glViewport(0, 0, mWidth, mHeight);
@@ -287,5 +288,6 @@
         return false;
     }
 
-    return eglSwapBuffers(mEglDisplay, mEglSurface);
+    EGL_RESULT_CHECK(eglSwapBuffers(mEglDisplay, mEglSurface)); 
+    return true;
 }
diff --git a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h
index caa1634..3c62a26 100644
--- a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h
+++ b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h
@@ -14,17 +14,18 @@
 #ifndef RENDERER_H
 #define RENDERER_H
 
-#include <android/native_window.h>
-
 #include <EGL/egl.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
 class Renderer {
 public:
-    Renderer(ANativeWindow* window, bool offscreen, int workload);
-    virtual bool setUp();
+    Renderer(EGLNativeWindowType window, bool offscreen);
+    virtual bool setUp(int workload);
     virtual bool tearDown();
+    bool eglSetUp();
+    void eglTearDown();
+
     bool draw();
     virtual void drawWorkload() = 0;
     virtual ~Renderer() {};
@@ -32,7 +33,6 @@
     static const int OFFSCREEN_GRID_SIZE = 10;
     bool mOffscreen;
 protected:
-    ANativeWindow* mWindow;
     EGLDisplay mEglDisplay;
     EGLSurface mEglSurface;
     EGLContext mEglContext;
@@ -40,7 +40,6 @@
     GLuint mProgramId;
     EGLint mWidth;
     EGLint mHeight;
-    int mWorkload;
     int mFboWidth;// Frame buffer width
     int mFboHeight;// Frame buffer height
     GLuint mFboId;// Frame buffer id
@@ -52,5 +51,7 @@
     GLuint mFboYOffsetUniformHandle;// Frame buffer y offset uniform handle
     GLuint mFboPositionHandle;// Frame buffer position handle
     GLuint mFboTexCoordHandle;// Frame buffer texture coordinate handle
+private:
+    EGLNativeWindowType mWindow;
 };
 #endif
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp b/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp
index 9d39af9..856da1e 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp
@@ -28,16 +28,24 @@
 
 // Holds the current benchmark's renderer.
 Renderer* gRenderer = NULL;
+ANativeWindow* gNativeWindow = NULL;
+
+enum {
+    FULL_PIPELINE_BENCHMARK = 0,
+    PIXEL_OUTPUT_BENCHMARK = 1,
+    SHADER_PERF_BENCHMARK = 2,
+    CONTEXT_SWITCH_BENCHMARK = 3
+};
 
 extern "C" JNIEXPORT jboolean JNICALL
 Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_startBenchmark(
-        JNIEnv* env, jclass clazz, jint numFrames, jdoubleArray frameTimes) {
+        JNIEnv* env, jclass /*clazz*/, jint workload, jint numFrames, jdoubleArray frameTimes) {
     if (gRenderer == NULL) {
         return false;
     }
 
     // Sets up the renderer.
-    bool success = gRenderer->setUp();
+    bool success = gRenderer->setUp(workload);
 
     // Records the start time.
     double start = GLUtils::currentTimeMillis();
@@ -60,41 +68,56 @@
     double times[] = {start, end};
     env->SetDoubleArrayRegion(frameTimes, 0, 2, times);
 
-    // Tears down and deletes the renderer.
     success = gRenderer->tearDown() && success;
-    delete gRenderer;
-    gRenderer = NULL;
     return success;
 }
 
 // The following functions create the renderers for the various benchmarks.
 extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupFullPipelineBenchmark(
-        JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
-    gRenderer = new FullPipelineRenderer(
-            ANativeWindow_fromSurface(env, surface), offscreen, workload);
-}
-
-extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupPixelOutputBenchmark(
-        JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
-    gRenderer = new PixelOutputRenderer(
-            ANativeWindow_fromSurface(env, surface), offscreen, workload);
-}
-
-extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupShaderPerfBenchmark(
-        JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
-    gRenderer = new ShaderPerfRenderer(
-            ANativeWindow_fromSurface(env, surface), offscreen, workload);
-}
-
-extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupContextSwitchBenchmark(
-        JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
-    if (workload <= 8) {
-        // This test uses 8 iterations, so workload can't be more than 8.
-        gRenderer = new ContextSwitchRenderer(
-                ANativeWindow_fromSurface(env, surface), offscreen, workload);
+Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupBenchmark(
+        JNIEnv* env, jclass /*clazz*/, jobject surface, jint benchmark,
+        jboolean offscreen) {
+    gNativeWindow = ANativeWindow_fromSurface(env, surface);
+    switch (benchmark) {
+        case FULL_PIPELINE_BENCHMARK:
+            gRenderer = new FullPipelineRenderer(gNativeWindow, offscreen);
+            break;
+        case PIXEL_OUTPUT_BENCHMARK:
+            gRenderer = new PixelOutputRenderer(gNativeWindow, offscreen);
+            break;
+        case SHADER_PERF_BENCHMARK:
+            gRenderer = new ShaderPerfRenderer(gNativeWindow, offscreen);
+            break;
+        case CONTEXT_SWITCH_BENCHMARK:
+            gRenderer = new ContextSwitchRenderer(gNativeWindow, offscreen);
+            break;
+        default:
+            ALOGE("Unknown benchmark '%d'", benchmark);
+            ANativeWindow_release(gNativeWindow);
+            gNativeWindow = NULL;
+            return;
     }
+
+    // Set up call will log error conditions
+    if (!gRenderer->eglSetUp()) {
+        delete gRenderer;
+        gRenderer = NULL;
+
+        ANativeWindow_release(gNativeWindow);
+        gNativeWindow = NULL;
+    }
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_tearDownBenchmark(
+        JNIEnv* /*env*/, jclass /*clazz*/) {
+    if (gRenderer == NULL) {
+        return;
+    }
+    gRenderer->eglTearDown();
+    delete gRenderer;
+    gRenderer = NULL;
+
+    ANativeWindow_release(gNativeWindow);
+    gNativeWindow = NULL;
 }
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp
index 7fd4093..1127d88 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp
@@ -74,13 +74,14 @@
         "  gl_FragColor = texture2D(u_Texture, v_TexCoord);"
         "}";
 
-ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window, bool offscreen, int workload) :
-        Renderer(window, offscreen, workload), mContexts(NULL) {
+ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window, bool offscreen) :
+        Renderer(window, offscreen), mContexts(NULL), mWorkload(0) {
 }
 
-bool ContextSwitchRenderer::setUp() {
+bool ContextSwitchRenderer::setUp(int workload) {
     SCOPED_TRACE();
-    if (!Renderer::setUp()) {
+    mWorkload = workload;
+    if (!Renderer::setUp(workload)) {
         return false;
     }
 
@@ -137,7 +138,7 @@
 bool ContextSwitchRenderer::tearDown() {
     SCOPED_TRACE();
     if (mContexts) {
-        // Destroy the contexts, the main one will be handled by Renderer::tearDown().
+        // Destroy the contexts, the main one will be handled by Renderer::eglTearDown().
         for (int i = 0; i < NUM_WORKER_CONTEXTS; i++) {
             if (mOffscreen) {
                 if (mFboIds[i] != 0) {
@@ -146,6 +147,7 @@
                     mFboIds[i] = 0;
                 }
             }
+            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
             eglDestroyContext(mEglDisplay, mContexts[i]);
         }
         delete[] mContexts;
@@ -163,6 +165,11 @@
 
 void ContextSwitchRenderer::drawWorkload() {
     SCOPED_TRACE();
+
+    if (mWorkload > 8) {
+        return; // This test does not support higher workloads.
+    }
+
     // Set the background clear color to black.
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
@@ -216,6 +223,7 @@
         }
     }
 
+    eglWaitSyncKHR(mEglDisplay, fence, 0);
     eglDestroySyncKHR(mEglDisplay, fence);
 
     // Switch back to the main context.
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h
index 51a4376..ae320ff 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h
@@ -18,9 +18,9 @@
 
 class ContextSwitchRenderer: public Renderer {
 public:
-    ContextSwitchRenderer(ANativeWindow* window, bool offscreen, int workload);
+    ContextSwitchRenderer(ANativeWindow* window, bool offscreen);
     virtual ~ContextSwitchRenderer() {};
-    bool setUp();
+    bool setUp(int workload);
     bool tearDown();
     void drawWorkload();
 private:
@@ -31,6 +31,7 @@
     GLuint mTranslateUniformHandle;
     GLuint mPositionHandle;
     GLuint mTexCoordHandle;
+    int mWorkload;
 };
 
 #endif
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
index 97462b5..0f75f81 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
@@ -91,15 +91,15 @@
         "  gl_FragColor = (diffuse * texture2D(u_Texture, v_TexCoordinate));\n"
         "}";
 
-FullPipelineRenderer::FullPipelineRenderer(ANativeWindow* window, bool offscreen, int workload) :
-        Renderer(window, offscreen, workload), mProgram(NULL), mSceneGraph(NULL),
+FullPipelineRenderer::FullPipelineRenderer(ANativeWindow* window, bool offscreen) :
+        Renderer(window, offscreen), mProgram(NULL), mSceneGraph(NULL),
         mModelMatrix(NULL), mViewMatrix(NULL), mProjectionMatrix(NULL), mMesh(NULL),
         mTextureId(0) {
 }
 
-bool FullPipelineRenderer::setUp() {
+bool FullPipelineRenderer::setUp(int workload) {
     SCOPED_TRACE();
-    if (!Renderer::setUp()) {
+    if (!Renderer::setUp(workload)) {
         return false;
     }
 
@@ -147,7 +147,7 @@
         return false;
     }
 
-    float count = mWorkload * mWorkload;
+    float count = workload * workload;
     float middle = count / 2.0f;
     float scale = 2.0f / count;
 
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
index 84616b4..ce44760 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
@@ -22,9 +22,9 @@
 
 class FullPipelineRenderer: public Renderer {
 public:
-    FullPipelineRenderer(ANativeWindow* window, bool offscreen, int workload);
+    FullPipelineRenderer(ANativeWindow* window, bool offscreen);
     virtual ~FullPipelineRenderer() {};
-    bool setUp();
+    bool setUp(int workload);
     bool tearDown();
     void drawWorkload();
 private:
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
index 287ebfb..3a3b9d1 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
@@ -50,13 +50,14 @@
         "  gl_FragColor = texture2D(u_Texture, v_TexCoord);"
         "}";
 
-PixelOutputRenderer::PixelOutputRenderer(ANativeWindow* window, bool offscreen, int workload) :
-        Renderer(window, offscreen, workload) {
+PixelOutputRenderer::PixelOutputRenderer(ANativeWindow* window, bool offscreen) :
+        Renderer(window, offscreen), mWorkload(0) {
 }
 
-bool PixelOutputRenderer::setUp() {
+bool PixelOutputRenderer::setUp(int workload) {
     SCOPED_TRACE();
-    if (!Renderer::setUp()) {
+    mWorkload = workload;
+    if (!Renderer::setUp(workload)) {
         return false;
     }
 
@@ -80,6 +81,11 @@
 
 bool PixelOutputRenderer::tearDown() {
     SCOPED_TRACE();
+    if (mProgramId != 0)
+    {
+        glDeleteProgram(mProgramId);
+        mProgramId = 0;
+    }
     if (mTextureId != 0) {
         glDeleteTextures(1, &mTextureId);
         mTextureId = 0;
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h
index e6b5692..816da6a 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h
@@ -18,9 +18,9 @@
 
 class PixelOutputRenderer: public Renderer {
 public:
-    PixelOutputRenderer(ANativeWindow* window, bool offscreen, int workload);
+    PixelOutputRenderer(ANativeWindow* window, bool offscreen);
     virtual ~PixelOutputRenderer() {};
-    bool setUp();
+    bool setUp(int workload);
     bool tearDown();
     void drawWorkload();
 private:
@@ -28,6 +28,7 @@
     GLuint mTextureUniformHandle;
     GLuint mPositionHandle;
     GLuint mTexCoordHandle;
+    int mWorkload;
 };
 
 #endif
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
index 1cbc839..a02f4fe 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
@@ -91,13 +91,13 @@
     return destAddr - destStart;
 }
 
-ShaderPerfRenderer::ShaderPerfRenderer(ANativeWindow* window, bool offscreen, int workload) :
-        Renderer(window, offscreen, workload) {
+ShaderPerfRenderer::ShaderPerfRenderer(ANativeWindow* window, bool offscreen) :
+        Renderer(window, offscreen) {
 }
 
-bool ShaderPerfRenderer::setUp() {
+bool ShaderPerfRenderer::setUp(int workload) {
     SCOPED_TRACE();
-    if (!Renderer::setUp()) {
+    if (!Renderer::setUp(workload)) {
         return false;
     }
 
@@ -106,7 +106,7 @@
     // Add the first part.
     int index = charCopy(SP_FRAGMENT_1, spFragment, 0);
     // Add the count, overwriting the '\0' added by charCopy.
-    spFragment[index - 1] = (char) (((int) '0') + mWorkload);
+    spFragment[index - 1] = (char) (((int) '0') + workload);
     // Add the second part.
     index += charCopy(SP_FRAGMENT_2, spFragment, index);
     // Create program.
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h
index 52fac43..c804202 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h
@@ -18,9 +18,9 @@
 
 class ShaderPerfRenderer: public Renderer {
 public:
-    ShaderPerfRenderer(ANativeWindow* window, bool offscreen, int workload);
+    ShaderPerfRenderer(ANativeWindow* window, bool offscreen);
     virtual ~ShaderPerfRenderer() {};
-    bool setUp();
+    bool setUp(int workload);
     void drawWorkload();
 private:
     GLuint mTextureId;
diff --git a/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp b/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp
index 1857848..dc0b4e2 100644
--- a/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp
+++ b/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp
@@ -23,7 +23,7 @@
 
 extern "C" JNIEXPORT jboolean JNICALL
 Java_com_android_cts_opengl_reference_GLGameActivity_startBenchmark(
-        JNIEnv* env, jclass clazz, jobject assetManager, jobject surface, jint numFrames,
+    JNIEnv* env, jclass /*clazz*/, jobject assetManager, jobject surface, jint numFrames,
         jdoubleArray setUpTimes, jdoubleArray updateTimes, jdoubleArray renderTimes) {
 
     GLUtils::setEnvAndAssetManager(env, assetManager);
@@ -32,9 +32,10 @@
         return false;
     }
 
-    ReferenceRenderer* renderer = new ReferenceRenderer(ANativeWindow_fromSurface(env, surface));
-
-    bool success = renderer->setUp();
+    ANativeWindow* nativeWindow = ANativeWindow_fromSurface(env, surface);
+    ReferenceRenderer* renderer = new ReferenceRenderer(nativeWindow);
+    bool success = renderer->eglSetUp();
+    success = renderer->setUp(0) && success;
     env->SetDoubleArrayRegion(
             setUpTimes, 0, ReferenceRenderer::NUM_SETUP_TIMES, renderer->mSetUpTimes);
 
@@ -54,7 +55,11 @@
     env->SetDoubleArrayRegion(renderTimes, 0, numFrames, renders);
 
     success = renderer->tearDown() && success;
+    renderer->eglTearDown();
     delete renderer;
     renderer = NULL;
+
+    ANativeWindow_release(nativeWindow);
+
     return success;
 }
diff --git a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
index 8f7703e..3b12ee1 100644
--- a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
@@ -22,10 +22,10 @@
 #include <Trace.h>
 
 ReferenceRenderer::ReferenceRenderer(ANativeWindow* window) :
-        Renderer(window, false, 0) {
+        Renderer(window, false) {
 }
 
-bool ReferenceRenderer::setUp() {
+bool ReferenceRenderer::setUp(int workload) {
     SCOPED_TRACE();
     // Reset the times.
     for (int i = 0; i < NUM_SETUP_TIMES; i++) {
@@ -33,7 +33,7 @@
     }
     // Set up OpenGLES.
     double start = GLUtils::currentTimeMillis();
-    if (!Renderer::setUp()) {
+    if (!Renderer::setUp(workload)) {
         return false;
     }
     mSetUpTimes[0] = GLUtils::currentTimeMillis() - start;
diff --git a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
index d10297a..f5c4b65 100644
--- a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
@@ -23,7 +23,7 @@
 public:
     ReferenceRenderer(ANativeWindow* window);
     virtual ~ReferenceRenderer() {};
-    bool setUp();
+    bool setUp(int workload);
     bool tearDown();
     bool update(int frame);
     void drawWorkload();
diff --git a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java
index 5dc9b88..6defdb7 100644
--- a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java
+++ b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java
@@ -102,19 +102,12 @@
         }
     }
 
-    private static native void setupFullPipelineBenchmark(
-            Surface surface, boolean offscreen, int workload);
+    private static native boolean setupBenchmark(
+            Surface surface, int benchmark, boolean offscreen);
 
-    private static native void setupPixelOutputBenchmark(
-            Surface surface, boolean offscreen, int workload);
+    private static native boolean startBenchmark(int workload, int numFrames, double[] frameTimes);
 
-    private static native void setupShaderPerfBenchmark(
-            Surface surface, boolean offscreen, int workload);
-
-    private static native void setupContextSwitchBenchmark(
-            Surface surface, boolean offscreen, int workload);
-
-    private static native boolean startBenchmark(int numFrames, double[] frameTimes);
+    private static native void tearDownBenchmark();
 
     /**
      * This thread runs the benchmarks, freeing the UI thread.
@@ -138,36 +131,29 @@
             watchDog = new WatchDog(mTimeout, this);
             // Used to record the start and end time of the iteration.
             double[] times = new double[2];
-            for (int i = 0; i < mNumIterations && success; i++) {
-                // The workload to use for this iteration.
-                int workload = i + 1;
+            try {
                 // Setup the benchmark.
-                switch (mBenchmark) {
-                    case FullPipeline:
-                        setupFullPipelineBenchmark(mSurface, mOffscreen, workload);
-                        break;
-                    case PixelOutput:
-                        setupPixelOutputBenchmark(mSurface, mOffscreen, workload);
-                        break;
-                    case ShaderPerf:
-                        setupShaderPerfBenchmark(mSurface, mOffscreen, workload);
-                        break;
-                    case ContextSwitch:
-                        setupContextSwitchBenchmark(mSurface, mOffscreen, workload);
-                        break;
-                }
-                watchDog.start();
-                // Start benchmark.
-                success = startBenchmark(mNumFrames, times);
-                watchDog.stop();
-
-                if (!success) {
-                    setException(new Exception("Benchmark failed to run"));
-                } else {
-                    // Calculate FPS.
-                    mFpsValues[i] = mNumFrames * 1000.0f / (times[1] - times[0]);
+                setupBenchmark(mSurface, mBenchmark.ordinal(), mOffscreen);
+                for (int i = 0; i < mNumIterations && success; i++) {
+                    // The workload to use for this iteration.
+                    int workload = i + 1;
+                    watchDog.start();
+                    // Start benchmark.
+                    success = startBenchmark(workload, mNumFrames, times);
+                    watchDog.stop();
+                    if (!success) {
+                        setException(new Exception("Benchmark failed to run"));
+                    } else {
+                        // Calculate FPS.
+                        mFpsValues[i] = mNumFrames * 1000.0f / (times[1] - times[0]);
+                    }
                 }
             }
+            finally
+            {
+                tearDownBenchmark();
+            }
+
             complete();
             Log.i(TAG, mBenchmark + " Benchmark Completed");
         }
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 754e38a..2cb99c4 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -1,5 +1,14 @@
 [
 {
+  description: "some AlarmClockTests are not robust across different device types",
+  names: [
+    "android.alarmclock.cts.DismissAlarmTest#testAll",
+    "android.alarmclock.cts.SetAlarmTest#testAll",
+    "android.alarmclock.cts.SnoozeAlarmTest#testAll"
+  ],
+  bug: 23776083
+},
+{
   description: "the UsageStats is not yet stable enough",
   names: [
     "android.app.usage.cts.UsageStatsTest"
@@ -15,6 +24,21 @@
   bug: 17595050
 },
 {
+  description: "test fails on devices with no telephony",
+  names: [
+    "android.calllog.cts.CallLogBackupTest#testSingleCallBackup"
+  ],
+  bug: 23776099
+},
+{
+  description: "test fails on some devices",
+  names: [
+    "android.dumpsys.cts.DumpsysHostTest#testBatterystatsOutput",
+    "android.dumpsys.cts.DumpsysHostTest#testGfxinfoFramestats"
+  ],
+  bug: 23776893
+},
+{
   description: "the SSLCertificateSocketFactoryTest often fails because of lack of live internet or short timeout, it should be refactored to do a local server testing",
   names: [
     "android.net.cts.SSLCertificateSocketFactoryTest#testCreateSocket",
@@ -39,6 +63,21 @@
   bug: 18461670
 },
 {
+  description: "test not robust",
+  names: [
+    "android.telecom.cts.ExtendedInCallServiceTest#testAddNewOutgoingCallAndThenDisconnect",
+    "android.telecom.cts.RemoteConferenceTest#testRemoteConferenceCallbacks_ConferenceableConnections"
+  ],
+  bug: 23604254
+},
+{
+  description: "tests too flaky",
+  names: [
+    "android.transition.cts.ChangeScrollTest#testChangeScroll"
+  ],
+  bug: 23779020
+},
+{
   description: "Not all jdwp features are currently supported. These tests will fail",
   names: [
     "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch001",
@@ -119,6 +158,21 @@
   bug: 17508787
 },
 {
+  description: "This test should be outside of official CTS suite until it is verified for all Nexus devices",
+  names: [
+    "com.android.cts.devicepolicy.MixedDeviceOwnerTest#testPackageInstallUserRestrictions",
+    "com.android.cts.devicepolicy.MixedProfileOwnerTest#testPackageInstallUserRestrictions"
+  ],
+  bug: 18928535
+},
+{
+  description: "Test is not yet properly implemented",
+  names: [
+    "android.voicesettings.cts.ZenModeTest#testAll"
+  ],
+  bug: 23238984
+},
+{
   description: "These tests fail on some devices.",
   names: [
     "android.uirendering.cts.testclasses.ExactCanvasTests#testBlueRect",
@@ -208,6 +262,13 @@
   bug: 23008511
 },
 {
+  description: "Light status bar CTS coming in late",
+  names: [
+    "com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons"
+  ],
+  bug: 23427621
+},
+{
   description: "known failures",
   names: [
     "android.hardware.cts.SensorBatchingTests#testAccelerometer_50hz_batching",
@@ -234,5 +295,70 @@
     "android.hardware.cts.SingleSensorTests#testOrientation_5hz"
   ],
   bug: 22922206
+},
+{
+  description: "tests are not yet ready",
+  names: [
+    "com.android.cts.app.os.OsHostTests#testNonExportedActivities"
+  ],
+  bug: 23779168
+},
+{
+  description: "New assist tests that do not yet have a track record.",
+  names: [
+    "android.assist.cts.AssistantContentViewTest",
+    "android.assist.cts.ExtraAssistDataTest",
+    "android.assist.cts.FocusChangeTest",
+    "android.assist.cts.LargeViewHierarchyTest",
+    "android.assist.cts.ScreenshotTest",
+    "android.assist.cts.TextViewTest",
+    "android.assist.cts.WebViewTest"
+  ],
+  bug: 21668302
+},
+{
+  description: "ConnectivityConstraintTest job scheduler not working.",
+  names: [
+     "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withWifi",
+     "android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintExecutes_withWifi"
+  ],
+  bug: 21262226
+},
+{
+   description: "ConnectivityConstraintTest times out.",
+   names: [
+     "android.jobscheduler.cts.TimingConstraintsTest#testJobParameters_unexpiredDeadline"
+   ],
+   bug: 23144425
+},
+{
+   description: "Telephony returning wrong value.",
+   names: [
+     "android.telephony.cts.CellInfoTest#testCellInfo"
+   ],
+   bug: 23979591
+},
+{
+   description: "Video encoding tests are timing out.",
+   names: [
+     "android.media.cts.VideoEncoderTest#testGoogH264FlexArbitraryW",
+     "android.media.cts.VideoEncoderTest#testGoogH264SurfArbitraryW"
+   ],
+   bug: 23827982
+},
+{
+  description: "tests not yet ready",
+  names: [
+    "android.telecom.cts.OutgoingCallTest#testStartCallWithSpeakerphoneFalse_SpeakerphoneOffInCall",
+    "android.telecom.cts.OutgoingCallTest#testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault"
+  ],
+  bug: 24067587
+},
+{
+  description: "protected broadcast not working",
+  names: [
+   "android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
+  ],
+  bug: 23192492
 }
 ]
diff --git a/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java b/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
index a86a707..241535e 100644
--- a/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
+++ b/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
@@ -16,6 +16,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.cts.jank.leanback.IntentKeys;
 import android.os.SystemClock;
 import android.support.test.jank.GfxMonitor;
@@ -40,9 +41,30 @@
     private final static String JAVA_PACKAGE = "android.cts.jank.leanback.ui";
     private final static String CLASS = JAVA_PACKAGE + ".MainActivity";
 
+    private boolean shouldSkip() {
+	PackageManager packageManager =
+                getInstrumentation().getTargetContext().getPackageManager();
+        if (!packageManager.hasSystemFeature(
+                PackageManager.FEATURE_LEANBACK)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void runTest() throws Throwable {
+        if (shouldSkip()) {
+            return;
+        }
+        super.runTest();
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        if (shouldSkip()) {
+            return;
+        }
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setComponent(new ComponentName(APP_PACKAGE, CLASS));
 
diff --git a/tests/tests/netlegacy22/Android.mk b/tests/netlegacy22.api/Android.mk
similarity index 100%
rename from tests/tests/netlegacy22/Android.mk
rename to tests/netlegacy22.api/Android.mk
diff --git a/tests/tests/netlegacy22/AndroidManifest.xml b/tests/netlegacy22.api/AndroidManifest.xml
similarity index 92%
rename from tests/tests/netlegacy22/AndroidManifest.xml
rename to tests/netlegacy22.api/AndroidManifest.xml
index d243e45..f13805c 100644
--- a/tests/tests/netlegacy22/AndroidManifest.xml
+++ b/tests/netlegacy22.api/AndroidManifest.xml
@@ -16,7 +16,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.net.legacy22">
+    package="com.android.cts.netlegacy22.api">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -30,7 +30,7 @@
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.cts.net.legacy22"
+                     android:targetPackage="com.android.cts.netlegacy22.api"
                      android:label="CTS tests of legacy android.net APIs as of API 22">
         <meta-data android:name="listener"
             android:value="com.android.cts.runner.CtsTestRunListener" />
diff --git a/tests/tests/netlegacy22/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
similarity index 84%
rename from tests/tests/netlegacy22/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
rename to tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
index 8a9002b..1836f06 100644
--- a/tests/tests/netlegacy22/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
+++ b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
@@ -85,24 +85,58 @@
                 ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
     }
 
-    private void checkSourceAddress(String addrString, int type) throws Exception {
-        DatagramSocket d = new DatagramSocket();
-        d.connect(InetAddress.getByName(addrString), 7);
-        InetAddress localAddress = d.getLocalAddress();
-
+    // Returns a list of all the IP addresses for all the networks of a given legacy type. We can't
+    // just fetch the IP addresses for that type because there is no public getLinkProperties API
+    // that takes a legacy type.
+    private List<InetAddress> getIpAddresses(int type) {
+        ArrayList<InetAddress> addresses = new ArrayList<>();
         Network[] networks = mCm.getAllNetworks();
         for (int i = 0; i < networks.length; i++) {
             NetworkInfo ni = mCm.getNetworkInfo(networks[i]);
             if (ni != null && ni.getType() == type) {
+                // This does not include IP addresses on stacked interfaces (e.g., 464xlat), because
+                // there is no public API that will return them.
                 LinkProperties lp = mCm.getLinkProperties(networks[i]);
                 for (LinkAddress address : lp.getLinkAddresses()) {
-                    if (address.getAddress().equals(localAddress)) {
-                        return;
-                    }
+                    addresses.add(address.getAddress());
                 }
             }
         }
-        fail("Local address " + localAddress + " not assigned to any network of type " + type);
+        return addresses;
+    }
+
+    private boolean hasIPv4(int type) {
+        for (InetAddress address : getIpAddresses(type)) {
+            if (address instanceof Inet4Address) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void checkSourceAddress(String addrString, int type) throws Exception {
+        // The public requestRouteToHost API only supports IPv4, but it will not return failure if
+        // the network does not have an IPv4 address. So don't check that it's working unless we
+        // know that the network has an IPv4 address. Note that it's possible that the network will
+        // have an IPv4 address but we don't know about it, because the IPv4 address might be on a
+        // stacked interface and we wouldn't be able to see it.
+        if (!hasIPv4(type)) {
+            Log.d(TAG, "Not checking source address on network type " + type + ", no IPv4 address");
+            return;
+        }
+
+        DatagramSocket d = new DatagramSocket();
+        d.connect(InetAddress.getByName(addrString), 7);
+        InetAddress localAddress = d.getLocalAddress();
+        String localAddrString = localAddress.getHostAddress();
+
+        Log.d(TAG, "Got source address " + localAddrString + " for destination " + addrString);
+
+        assertTrue(
+                "Local address " + localAddress + " not assigned to any network of type " + type,
+                getIpAddresses(type).contains(localAddress));
+
+        Log.d(TAG, "Source address " + localAddress + " found on network type " + type);
     }
 
     /** Test that hipri can be brought up when Wifi is enabled. */
@@ -127,7 +161,6 @@
         assertTrue("Couldn't requestRouteToHost using HIPRI.",
                 mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
 
-        try { Thread.sleep(1000); } catch(Exception e) {}
         checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
         checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
 
diff --git a/tests/netlegacy22.permission/Android.mk b/tests/netlegacy22.permission/Android.mk
new file mode 100644
index 0000000..fff9d85
--- /dev/null
+++ b/tests/netlegacy22.permission/Android.mk
@@ -0,0 +1,32 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsNetTestCasesLegacyPermission22
+
+LOCAL_SDK_VERSION := 22
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/netlegacy22.permission/AndroidManifest.xml b/tests/netlegacy22.permission/AndroidManifest.xml
new file mode 100644
index 0000000..cd1d2ba
--- /dev/null
+++ b/tests/netlegacy22.permission/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.netlegacy22.permission">
+
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.permission.cts.PermissionStubActivity"
+                  android:label="PermissionStubActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <!--
+        The CTS stubs package cannot be used as the target application here,
+        since that requires many permissions to be set. Instead, specify this
+        package itself as the target and include any stub activities needed.
+
+        This test package uses the default InstrumentationTestRunner, because
+        the InstrumentationCtsTestRunner is only available in the stubs
+        package. That runner cannot be added to this package either, since it
+        relies on hidden APIs.
+    -->
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.netlegacy22.permission"
+                     android:label="CTS tests of legacy android.net permissions as of API 22">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
new file mode 100644
index 0000000..0e59288
--- /dev/null
+++ b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts.legacy.api22.permission;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+* Test that protected android.net.ConnectivityManager methods cannot be called without
+* permissions
+*/
+public class ConnectivityManagerPermissionTest extends AndroidTestCase {
+
+    private ConnectivityManager mConnectivityManager = null;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mConnectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        assertNotNull(mConnectivityManager);
+    }
+
+    /**
+     * Verify that calling {@link ConnectivityManager#requestRouteToHost(int, int)}
+     * requires permissions.
+     * <p>Tests Permission:
+     *   {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     */
+    @SmallTest
+    public void testRequestRouteToHost() {
+        try {
+            mConnectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, 1);
+            fail("Was able to call requestRouteToHost");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+}
diff --git a/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
new file mode 100644
index 0000000..8547261
--- /dev/null
+++ b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts.legacy.api22.permission;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import java.net.InetAddress;
+
+/**
+ * Verify ConnectivityManager related methods without specific network state permissions.
+ */
+public class NoNetworkStatePermissionTest extends AndroidTestCase {
+    private ConnectivityManager mConnectivityManager;
+    private static final int TEST_NETWORK_TYPE = ConnectivityManager.TYPE_MOBILE;
+    private static final String TEST_FEATURE = "enableHIPRI";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        assertNotNull(mConnectivityManager);
+    }
+
+    /**
+     * Verify that ConnectivityManager#startUsingNetworkFeature() requires permissions.
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     */
+    @SmallTest
+    public void testStartUsingNetworkFeature() {
+        try {
+            mConnectivityManager.startUsingNetworkFeature(TEST_NETWORK_TYPE, TEST_FEATURE);
+            fail("ConnectivityManager.startUsingNetworkFeature didn't throw SecurityException as"
+                    + " expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Verify that ConnectivityManager#requestRouteToHost() requires permissions.
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     */
+    @SmallTest
+    public void testRequestRouteToHost() {
+        try {
+            mConnectivityManager.requestRouteToHost(TEST_NETWORK_TYPE, 0xffffffff);
+            fail("ConnectivityManager.requestRouteToHost didn't throw SecurityException as"
+                    + " expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+}
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 44ab12e..f5b29cf 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -24,8 +24,10 @@
 import android.app.Service;
 import android.app.UiAutomation;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
 import android.widget.EditText;
@@ -44,6 +46,8 @@
 public class AccessibilityEndToEndTest extends
         AccessibilityActivityTestCase<AccessibilityEndToEndActivity> {
 
+    private static final String LOG_TAG = "AccessibilityEndToEndTest";
+
     /**
      * Creates a new instance for testing {@link AccessibilityEndToEndActivity}.
      */
@@ -309,6 +313,14 @@
     @MediumTest
     @SuppressWarnings("deprecation")
     public void testTypeNotificationStateChangedAccessibilityEvent() throws Throwable {
+        // No notification UI on televisions.
+        if((getActivity().getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) {
+            Log.i(LOG_TAG, "Skipping: testTypeNotificationStateChangedAccessibilityEvent" +
+                    " - No notification UI on televisions.");
+            return;
+        }
+
         String message = getActivity().getString(R.string.notification_message);
 
         // create the notification to send
diff --git a/tests/tests/alarmclock/AndroidTest.xml b/tests/tests/alarmclock/AndroidTest.xml
index 1cdd7f4..aafdb61 100644
--- a/tests/tests/alarmclock/AndroidTest.xml
+++ b/tests/tests/alarmclock/AndroidTest.xml
@@ -17,7 +17,5 @@
     <option name="cts-apk-installer:test-file-name" value="CtsAlarmClockService.apk" />
     <option name="run-command:run-command"
          value="settings put secure voice_interaction_service android.alarmclock.service/.MainInteractionService" />
-    <option name="run-command:teardown-command"
-         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
     <option name="cts-apk-installer:test-file-name" value="CtsAlarmClockTestCases.apk" />
 </configuration>
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
index 2ce57d3..f41100a 100644
--- a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
@@ -23,6 +23,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.AlarmClock;
 import android.os.Bundle;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
@@ -74,8 +76,40 @@
                 new IntentFilter(Utils.BROADCAST_INTENT + testCaseType.toString()));
     }
 
+    private boolean isIntentSupported(TestcaseType testCaseType) {
+        Intent intent;
+        switch (testCaseType) {
+          case DISMISS_ALARM:
+              intent = new Intent(AlarmClock.ACTION_DISMISS_ALARM);
+              break;
+
+          case SET_ALARM:
+          case SET_ALARM_FOR_DISMISSAL:
+              intent = new Intent(AlarmClock.ACTION_SET_ALARM);
+              break;
+
+          case SNOOZE_ALARM:
+              intent = new Intent(AlarmClock.ACTION_SNOOZE_ALARM);
+              break;
+
+          default:
+              // shouldn't happen
+              return false;
+        }
+        final PackageManager manager = mContext.getPackageManager();
+        assertNotNull(manager);
+        if (manager.resolveActivity(intent, 0) == null) {
+            Log.i(TAG, "No Voice Activity found for the intent: " + intent.getAction());
+            return false;
+        }
+        return true;
+    }
+
     protected String runTest(TestcaseType testCaseType) throws Exception {
         Log.i(TAG, "Begin Testing: " + testCaseType);
+        // Make sure the corresponding intent is supported by the platform, before testing.
+        if (!isIntentSupported(testCaseType)) return Utils.COMPLETION_RESULT;
+
         if (!startTestActivity(testCaseType)) {
             fail("test activity start failed for testcase = " + testCaseType);
             return "";
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index dfa278a..c8ef253 100644
--- a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
+++ b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.DisplayMetrics;
@@ -40,6 +41,8 @@
     }
 
     public static class ExpectedMemorySizesClass {
+        private static final Map<Integer, Integer> expectedMemorySizeForWatch
+            =  new HashMap<Integer, Integer>();
         private static final Map<Integer, Integer> expectedMemorySizeForSmallNormalScreen
             =  new HashMap<Integer, Integer>();
         private static final Map<Integer, Integer> expectedMemorySizeForLargeScreen
@@ -48,6 +51,21 @@
             =  new HashMap<Integer, Integer>();
 
         static {
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_LOW, 32);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_MEDIUM, 32);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_TV, 32);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_HIGH, 36);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_280, 36);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XHIGH, 48);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_360, 48);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_400, 56);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_420, 64);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XXHIGH, 88);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_560, 112);
+            expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XXXHIGH, 154);
+        }
+
+        static {
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_LOW, 32);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_MEDIUM, 32);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_TV, 48);
@@ -92,7 +110,15 @@
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XXXHIGH, 768);
         }
 
-        public static Integer getExpectedMemorySize(int screenSize, int screenDensity) {
+        public static Integer getExpectedMemorySize(
+                int screenSize,
+                int screenDensity,
+                boolean isWatch) {
+
+           if (isWatch) {
+              return expectedMemorySizeForWatch.get(screenDensity);
+           }
+
            switch (screenSize) {
                 case Configuration.SCREENLAYOUT_SIZE_SMALL:
                 case Configuration.SCREENLAYOUT_SIZE_NORMAL:
@@ -141,8 +167,11 @@
     }
 
     private void assertMemoryForScreenDensity(int memoryClass, int screenDensity, int screenSize) {
-        int expectedMinimumMemory = ExpectedMemorySizesClass.getExpectedMemorySize(screenSize,
-                                                                                   screenDensity);
+        Context context = getInstrumentation().getTargetContext();
+        boolean isWatch =
+            context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+        int expectedMinimumMemory =
+            ExpectedMemorySizesClass.getExpectedMemorySize(screenSize, screenDensity, isWatch);
 
         assertTrue("Expected to have at least " + expectedMinimumMemory
                 + "mb of memory for screen density " + screenDensity,
diff --git a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
index 5781442..fbb3060 100644
--- a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -74,9 +74,8 @@
         sendNotification(id, R.drawable.black);
         mNotificationManager.cancel(id);
 
-        StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
-        for (StatusBarNotification sbn : sbns) {
-            assertFalse("canceled notification was still alive, id=" + id, sbn.getId() == id);
+        if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
+            fail("canceled notification was still alive, id=" + id);
         }
     }
 
@@ -116,10 +115,31 @@
                 .build();
         mNotificationManager.notify(id, notification);
 
-        StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
-        for (StatusBarNotification sbn : sbns) {
-            if (sbn.getId() == id) return;
+
+        if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
+            fail("couldn't find posted notification id=" + id);
         }
-        fail("couldn't find posted notification id=" + id);
+    }
+
+    private boolean checkNotificationExistence(int id, boolean shouldExist) {
+        // notification is a bit asynchronous so it may take a few ms to appear in getActiveNotifications()
+        // we will check for it for up to 200ms before giving up
+        boolean found = false;
+        for (int tries=3; tries-->0;) {
+            final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+            for (StatusBarNotification sbn : sbns) {
+                if (sbn.getId() == id) {
+                    found = true;
+                    break;
+                }
+            }
+            if (found == shouldExist) break;
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ex) {
+                // pass
+            }
+        }
+        return found == shouldExist;
     }
 }
diff --git a/tests/tests/assist/AndroidManifest.xml b/tests/tests/assist/AndroidManifest.xml
index 97bd874..a81ced2 100644
--- a/tests/tests/assist/AndroidManifest.xml
+++ b/tests/tests/assist/AndroidManifest.xml
@@ -20,16 +20,22 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
+    <uses-permission android:name="android.permission.INTERNET" />
 
     <application>
       <uses-library android:name="android.test.runner" />
-      <activity android:name="android.assist.TestStartActivity"
-                android:label="Assist Target">
+      <activity android:name="android.assist.cts.TestStartActivity"
+                android:label="Assist Test Start Activity">
           <intent-filter>
               <action android:name="android.intent.action.TEST_START_ACTIVITY_ASSIST_STRUCTURE" />
               <action android:name="android.intent.action.TEST_START_ACTIVITY_DISABLE_CONTEXT" />
               <action android:name="android.intent.action.TEST_START_ACTIVITY_FLAG_SECURE" />
               <action android:name="android.intent.action.TEST_START_ACTIVITY_LIFECYCLE" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_SCREENSHOT" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_EXTRA_ASSIST" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_TEXTVIEW" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_LARGE_VIEW_HIERARCHY" />
+              <action android:name="android.intent.action.TEST_START_ACTIVITY_WEBVIEW" />
               <category android:name="android.intent.category.LAUNCHER" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
diff --git a/tests/tests/assist/AndroidTest.xml b/tests/tests/assist/AndroidTest.xml
index f7c56ad..329692d 100644
--- a/tests/tests/assist/AndroidTest.xml
+++ b/tests/tests/assist/AndroidTest.xml
@@ -19,7 +19,5 @@
     <option name="cts-apk-installer:test-file-name" value="CtsAssistApp.apk" />
     <option name="run-command:run-command"
          value="settings put secure voice_interaction_service android.assist.service/.MainInteractionService" />
-    <option name="run-command:teardown-command"
-         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
     <option name="cts-apk-installer:test-file-name" value="CtsAssistTestCases.apk" />
 </configuration>
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
index 8d555da..54416b4 100644
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -19,6 +19,8 @@
 import android.content.ComponentName;
 import android.os.Bundle;
 
+import org.json.JSONException;
+import org.json.JSONObject;
 import java.util.ArrayList;
 
 public class Utils {
@@ -29,13 +31,24 @@
     public static final String BROADCAST_ASSIST_DATA_INTENT = ACTION_PREFIX + "ASSIST_DATA";
     public static final String BROADCAST_INTENT_START_ASSIST = ACTION_PREFIX + "START_ASSIST";
     public static final String ASSIST_RECEIVER_REGISTERED = ACTION_PREFIX + "ASSIST_READY";
+
+    public static final String ACTION_INVALIDATE = "invalidate_action";
+    public static final String GET_CONTENT_VIEW_HEIGHT = ACTION_PREFIX + "GET_CONTENT_VIEW_HEIGHT";
+    public static final String BROADCAST_CONTENT_VIEW_HEIGHT = ACTION_PREFIX + "VIEW_HEIGHT";
+    public static final String SCROLL_TEXTVIEW_ACTION = ACTION_PREFIX + "TEXTVIEW_SCROLL";
+    public static final String SCROLL_SCROLLVIEW_ACTION = ACTION_PREFIX + "SCROLLVIEW_SCROLL";
     public static final String TEST_ERROR = "Error In Test:";
 
     public static final String ASSIST_STRUCTURE_KEY = "assist_structure";
     public static final String ASSIST_CONTENT_KEY = "assist_content";
     public static final String ASSIST_BUNDLE_KEY = "assist_bundle";
     public static final String ASSIST_SCREENSHOT_KEY = "assist_screenshot";
-
+    public static final String SCREENSHOT_COLOR_KEY = "set_screenshot_color";
+    public static final String COMPARE_SCREENSHOT_KEY = "compare_screenshot";
+    public static final String DISPLAY_WIDTH_KEY = "display_width";
+    public static final String DISPLAY_HEIGHT_KEY = "dislay_height";
+    public static final String SCROLL_X_POSITION = "scroll_x_position";
+    public static final String SCROLL_Y_POSITION = "scroll_y_position";
 
     /** Lifecycle Test intent constants */
     public static final String LIFECYCLE_PREFIX = ACTION_PREFIX + "lifecycle_";
@@ -44,34 +57,103 @@
     public static final String LIFECYCLE_ONSTOP = LIFECYCLE_PREFIX + "onstop";
     public static final String LIFECYCLE_ONDESTROY = LIFECYCLE_PREFIX + "ondestroy";
 
+    /** Focus Change Test intent constants */
+    public static final String GAINED_FOCUS = ACTION_PREFIX + "focus_changed";
+    public static final String LOST_FOCUS = ACTION_PREFIX + "lost_focus";
+
     /** Flag Secure Test intent constants */
     public static final String FLAG_SECURE_HASRESUMED = ACTION_PREFIX + "flag_secure_hasResumed";
+    public static final String APP_3P_HASRESUMED = ACTION_PREFIX + "app_3p_hasResumed";
+    public static final String TEST_ACTIVITY_LOADED = ACTION_PREFIX + "test_activity_hasResumed";
 
     /** Two second timeout for getting back assist context */
-    public static final int TIMEOUT_MS = 2 * 1000; // TODO(awlee): what is the timeout
+    public static final int TIMEOUT_MS = 2 * 1000;
+    /** Four second timeout for an activity to resume */
     public static final int ACTIVITY_ONRESUME_TIMEOUT_MS = 4000;
+
     public static final String EXTRA_REGISTER_RECEIVER = "register_receiver";
 
+    /** Extras for passing the Assistant's ContentView's dimensions*/
+    public static final String EXTRA_CONTENT_VIEW_HEIGHT = "extra_content_view_height";
+    public static final String EXTRA_CONTENT_VIEW_WIDTH = "extra_content_view_width";
+    public static final String EXTRA_DISPLAY_POINT = "extra_display_point";
+
     /** Test name suffixes */
     public static final String ASSIST_STRUCTURE = "ASSIST_STRUCTURE";
     public static final String DISABLE_CONTEXT = "DISABLE_CONTEXT";
     public static final String FLAG_SECURE = "FLAG_SECURE";
     public static final String LIFECYCLE = "LIFECYCLE";
+    public static final String SCREENSHOT = "SCREENSHOT";
+    public static final String EXTRA_ASSIST = "EXTRA_ASSIST";
+    public static final String VERIFY_CONTENT_VIEW = "VERIFY_CONTENT_VIEW";
+    public static final String TEXTVIEW = "TEXTVIEW";
+    public static final String LARGE_VIEW_HIERARCHY = "LARGE_VIEW_HIERARCHY";
+    public static final String WEBVIEW = "WEBVIEW";
+    public static final String FOCUS_CHANGE = "FOCUS_CHANGE";
 
     /** Session intent constants */
     public static final String HIDE_SESSION = "android.intent.action.hide_session";
 
-    /**
-     * The shim activity that starts the service associated with each test.
-     */
+    /** Stub html view to load into WebView */
+    public static final String WEBVIEW_HTML_GREETING = "Hello WebView!";
+    public static final String WEBVIEW_HTML = "<html><body><div><p>" + WEBVIEW_HTML_GREETING
+            + "</p></div></body></html>";
+
+    /** Extra data to add to assist data and assist content */
+    private static Bundle EXTRA_ASSIST_BUNDLE;
+    private static String STRUCTURED_JSON;
+
+    public static final String getStructuredJSON() throws Exception {
+        if (STRUCTURED_JSON == null) {
+            STRUCTURED_JSON = new JSONObject()
+                    .put("@type", "MusicRecording")
+                    .put("@id", "https://example/music/recording")
+                    .put("url", "android-app://com.example/https/music/album")
+                    .put("name", "Album Title")
+                    .put("hello", "hi there")
+                    .put("knownNull", null)
+                    .put("unicode value", "\ud800\udc35")
+                    .put("empty string", "")
+                    .put("LongString",
+                        "lkasdjfalsdkfjalsdjfalskj9i9234jl1w23j4o123j412l3j421l3kj412l3kj1l3k4j32")
+                    .put("\ud800\udc35", "any-value")
+                    .put("key with spaces", "any-value")
+                    .toString();
+        }
+        return STRUCTURED_JSON;
+    }
+
+    public static final Bundle getExtraAssistBundle() {
+        if (EXTRA_ASSIST_BUNDLE == null) {
+            EXTRA_ASSIST_BUNDLE = new Bundle();
+            addExtraAssistDataToBundle(EXTRA_ASSIST_BUNDLE);
+        }
+        return EXTRA_ASSIST_BUNDLE;
+    }
+
+    public static void addExtraAssistDataToBundle(Bundle data) {
+        data.putString("hello", "there");
+        data.putBoolean("isthis_true_or_false", true);
+        data.putInt("number", 123);
+    }
+
+    /** The shim activity that starts the service associated with each test. */
     public static final String getTestActivity(String testCaseType) {
         switch (testCaseType) {
-            case ASSIST_STRUCTURE:
-                return "service.AssistStructureActivity";
             case DISABLE_CONTEXT:
+                // doesn't need to wait for activity to resume
+                // can be activated on top of any non-secure activity.
                 return "service.DisableContextActivity";
+            case ASSIST_STRUCTURE:
             case FLAG_SECURE:
             case LIFECYCLE:
+            case SCREENSHOT:
+            case EXTRA_ASSIST:
+            case VERIFY_CONTENT_VIEW:
+            case TEXTVIEW:
+            case LARGE_VIEW_HIERARCHY:
+            case WEBVIEW:
+            case FOCUS_CHANGE:
                 return "service.DelayedAssistantActivity";
             default:
                 return "";
@@ -84,6 +166,9 @@
     public static final ComponentName getTestAppComponent(String testCaseType) {
         switch (testCaseType) {
             case ASSIST_STRUCTURE:
+            case LARGE_VIEW_HIERARCHY:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.TestApp");
             case DISABLE_CONTEXT:
                 return new ComponentName(
                         "android.assist.testapp", "android.assist.testapp.TestApp");
@@ -93,11 +178,39 @@
             case LIFECYCLE:
                 return new ComponentName(
                         "android.assist.testapp", "android.assist.testapp.LifecycleActivity");
+            case SCREENSHOT:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.ScreenshotActivity");
+            case EXTRA_ASSIST:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.ExtraAssistDataActivity");
+            case TEXTVIEW:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.TextViewActivity");
+            case WEBVIEW:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.WebViewActivity");
+            case FOCUS_CHANGE:
+                return new ComponentName(
+                        "android.assist.testapp", "android.assist.testapp.FocusChangeActivity");
             default:
                 return new ComponentName("","");
         }
     }
 
+    /**
+     * Returns the amount of time to wait for assist data.
+     */
+    public static final int getAssistDataTimeout(String testCaseType) {
+        switch (testCaseType) {
+            case SCREENSHOT:
+                // needs to wait for 3p activity to resume before receiving assist data.
+                return TIMEOUT_MS + ACTIVITY_ONRESUME_TIMEOUT_MS;
+            default:
+                return TIMEOUT_MS;
+        }
+    }
+
     public static final String toBundleString(Bundle bundle) {
         if (bundle == null) {
             return "*** Bundle is null ****";
diff --git a/tests/tests/assist/res/layout/multiple_text_views.xml b/tests/tests/assist/res/layout/multiple_text_views.xml
new file mode 100644
index 0000000..455d5e3
--- /dev/null
+++ b/tests/tests/assist/res/layout/multiple_text_views.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:orientation="vertical">
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <ScrollView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1">
+    <TextView
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:scrollbars="vertical"
+      android:text="@string/text_too_large_to_fit" />
+  </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/screenshot_activity.xml b/tests/tests/assist/res/layout/screenshot_activity.xml
new file mode 100644
index 0000000..05051dc
--- /dev/null
+++ b/tests/tests/assist/res/layout/screenshot_activity.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/screenshot_activity"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/test_app.xml b/tests/tests/assist/res/layout/test_app.xml
new file mode 100644
index 0000000..3fbfd6d
--- /dev/null
+++ b/tests/tests/assist/res/layout/test_app.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/welcome" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="350dp"
+        android:orientation="vertical"
+        android:layout_gravity="bottom">
+        <FrameLayout
+            android:id="@+id/card1"
+            android:layout_width="match_parent"
+            android:layout_height="150dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginTop="16dp"
+            android:elevation="3dp">
+        </FrameLayout>
+        <View
+            android:id="@+id/card2"
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginTop="16dp"
+            android:elevation="3dp"/>
+    </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/text_view.xml b/tests/tests/assist/res/layout/text_view.xml
new file mode 100644
index 0000000..9964ab6
--- /dev/null
+++ b/tests/tests/assist/res/layout/text_view.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:orientation="vertical">
+    <TextView
+        android:id="@+id/text_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:focusable="false"
+        android:focusableInTouchMode="false"
+        android:scrollbars="vertical"
+        android:clickable="true"
+        android:text="@string/text_too_large_to_fit" />
+
+    <ScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:scrollbars="vertical"
+            android:text="@string/text_too_large_to_fit" />
+    </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/webview.xml b/tests/tests/assist/res/layout/webview.xml
new file mode 100644
index 0000000..bdb8082
--- /dev/null
+++ b/tests/tests/assist/res/layout/webview.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    </WebView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/values/strings.xml b/tests/tests/assist/res/values/strings.xml
new file mode 100644
index 0000000..8170d70
--- /dev/null
+++ b/tests/tests/assist/res/values/strings.xml
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string name="welcome">Hello there!</string>
+    <string name="testAppTitle">Assist Structure Test Activity</string>
+    <string name="screenshotActivityTitle">Screenshot Test Activity</string>
+    <string name="textViewActivityTitle">TextView Test Activity</string>
+    <string name="webViewActivityTitle">WebView Test Activity</string>
+    <string name="text_too_large_to_fit">❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € →Hello هتاف للترحيب שלום
+        përshëndetje Добры дзень 您好 হ্যালো здравей მიესალმები Χαίρετε હેલો नमस्ते Nnọọ こんにちは ಹಲೋ
+        Сәлеметсіз бе ជំរាបសួរ 안녕하세요 ສະບາຍດີ ഹലോ हॅलो Сайн байна уу नमस्ते سلامהעלא ہیلو
+        မင်္ဂလာပါ ਸਤ ਸ੍ਰੀ ਅਕਾਲ Здравствуйте здраво ආයුබෝවන් ஹலோ హలో สวัสดี Pẹlẹ o
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+        convallis nunc et vestibulum. Sed et consequat quam, blandit varius tortor. Curabitur
+        accumsan nulla lectus, placerat condimentum odio elementum vel. Nulla erat ex, accumsan ut
+        enim sagittis, scelerisque efficitur ante. Nullam quis orci nec magna maximus malesuada ac
+        id sem. Nam sagittis erat risus, a accumsan neque congue sit amet. Nullam risus velit,
+        faucibus eget scelerisque et, maximus eget arcu. Sed porta sed libero ac imperdiet.
+
+        Nulla sem lectus, ullamcorper id dui vel, rutrum interdum augue. Proin aliquam nisi vitae
+        hendrerit tempor. Mauris porttitor velit et egestas feugiat. Vivamus eu dapibus libero,
+        quis fringilla urna. Suspendisse non turpis dui. Vivamus facilisis diam vitae est auctor
+        luctus. Etiam quis lectus viverra, interdum turpis eu, aliquam sem. Nulla vulputate lacinia
+        nisi a dictum. Cras faucibus vitae tortor at ullamcorper. Quisque sit amet sapien maximus,
+        ornare nisi non, imperdiet magna. Vestibulum tempor metus ac mi ultrices dapibus.
+
+        Suspendisse potenti. Mauris pellentesque lacinia tristique. Pellentesque vel dui quis sem
+        lacinia imperdiet feugiat vitae sem. Proin a arcu magna. Sed quis augue eu mi accumsan
+        pellentesque pretium in leo. Duis euismod purus mauris, ac tempor erat auctor non. Quisque
+        bibendum est pulvinar ex dapibus, ac tincidunt nibh tempus. Mauris sodales sem id purus
+        commodo iaculis. Pellentesque a quam dapibus, vehicula lectus at, tincidunt arcu. In
+        placerat porttitor urna quis consequat. Nullam feugiat nisl sed urna hendrerit, sed
+        elementum massa iaculis. Fusce sit amet turpis hendrerit, varius lorem sed, luctus mi.
+        Phasellus sit amet ex orci. Duis scelerisque nisl quis efficitur maximus. Curabitur vitae
+        accumsan nunc, eget varius nisi.
+
+        Fusce efficitur malesuada luctus. Aliquam dapibus tortor sit amet purus semper, sit amet
+        pretium lorem feugiat. Maecenas gravida sed arcu et placerat. Nulla facilisi. Cras placerat
+        rutrum mi, in rutrum mauris maximus at. Mauris eu suscipit ante. Nullam pharetra egestas
+        diam a viverra. Donec sem turpis, tempor malesuada est vel, blandit accumsan magna. In
+        iaculis velit in efficitur hendrerit. Nulla facilisi. Curabitur eget ligula lorem. Sed sit
+        amet dolor ut ligula malesuada condimentum. Phasellus molestie augue eget libero commodo,
+        vel blandit ex blandit.
+
+        Morbi cursus tortor ante, et tempus nisi tempus et. Suspendisse quis gravida diam. Aliquam
+        efficitur dolor sit amet sollicitudin varius. Etiam libero purus, ornare nec nulla vel,
+        ullamcorper blandit nisl. Sed vel consequat diam, id placerat sem. Donec quis elementum
+        urna. In posuere bibendum nunc, in condimentum justo blandit ac. Quisque enim lorem,
+        gravida at purus at, sollicitudin imperdiet erat. Ut consectetur rutrum ante, et pretium
+        odio iaculis a. Nullam a nibh vulputate, volutpat lectus eu, pellentesque felis. Nam
+        vehicula suscipit diam nec convallis. Quisque congue maximus sem, sit amet hendrerit leo
+        tempor et.
+
+        Nam eu consequat dui. Sed semper dignissim mattis. Integer tortor eros, tempor in lectus a,
+        lobortis aliquam dolor. Phasellus at sagittis magna. Nulla eleifend orci ac urna auctor,
+        sit amet luctus urna vulputate. Nulla venenatis venenatis erat ac finibus. Etiam
+        ullamcorper elementum suscipit. Morbi nec velit non mauris porta finibus. Nullam in
+        sagittis odio. Praesent eget nisl ut mauris vestibulum feugiat. Sed vulputate at elit et
+        cursus. Praesent viverra erat blandit nunc egestas, vel feugiat ex condimentum. Class
+        aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+
+        Nulla fermentum mattis urna, non gravida eros vestibulum et. Fusce porttitor augue turpis,
+        sit amet aliquam augue sodales non. Nunc neque odio, sagittis sed gravida euismod, commodo
+        at libero. Donec porttitor pulvinar neque vitae lobortis. Aliquam accumsan velit nec sapien
+        placerat egestas. Aliquam at tincidunt massa, et dignissim justo. Donec sapien ante, rutrum
+        et tristique a, commodo a massa.
+
+        Nunc placerat lobortis magna, et molestie lacus semper porta. Lorem ipsum dolor sit amet,
+        consectetur adipiscing elit. Phasellus ac ligula dui. Duis ultrices viverra eros fermentum
+        finibus. Integer ultrices, felis in accumsan volutpat, mi ligula hendrerit nunc, nec
+        accumsan mauris tellus sit amet metus. Ut pharetra enim et sapien sollicitudin, nec
+        ultricies urna pharetra. Morbi non tortor nec dui feugiat rutrum. Aliquam malesuada sodales
+        risus, sed congue nunc accumsan vitae. Etiam nunc magna, tempus non suscipit eu, feugiat ut
+        nibh. Maecenas et libero ut nisl pellentesque tempor nec vel quam. Etiam sem ligula,
+        ullamcorper non dolor a, viverra placerat nulla. Nullam dictum commodo dui, sed ultrices
+        enim sagittis eget. Morbi non consectetur lectus. In gravida, augue vitae pulvinar
+        molestie, ligula orci vulputate ex, at bibendum urna felis nec nibh. Sed nisl nunc, iaculis
+        at augue venenatis, fringilla accumsan velit. Curabitur nec augue porttitor, rutrum nisi
+        vitae, elementum orci.
+
+        Vestibulum eu tortor iaculis, dignissim velit quis, rhoncus dolor. Donec et tincidunt
+        nulla. Duis faucibus auctor erat ac ultricies. In a fermentum mi. Fusce vitae mi id sem
+        interdum tincidunt. Nulla hendrerit orci turpis, in maximus elit mollis eget. Aliquam erat
+        volutpat. Phasellus mattis est nibh, ut scelerisque ligula egestas eu. Ut molestie orci a
+        malesuada tempor. Sed tempus arcu id orci gravida faucibus. Vivamus ac lacinia neque, at
+        vehicula magna.
+
+        Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Nullam aliquam, justo scelerisque egestas sodales, purus odio posuere arcu, ac ultrices
+        nunc felis non massa. Aliquam vulputate ipsum sed aliquet auctor. In pulvinar, eros sit
+        amet ultricies tristique, lacus ipsum scelerisque eros, nec vestibulum est lectus quis
+        lorem. Pellentesque ac augue ut eros mattis viverra vitae ut lacus. Phasellus imperdiet
+        efficitur elit eget tincidunt. Donec sodales metus at dolor pulvinar, at gravida nibh
+        facilisis. Sed nec tellus luctus, cursus lacus sed, euismod orci. Maecenas sit amet leo
+        orci. Nulla non leo non mi eleifend consequat sit amet vitae dui. Sed gravida gravida
+        justo, tincidunt ultrices justo semper vitae. Fusce at nisi nisl. Morbi molestie quis justo
+        a convallis. Curabitur massa lacus, feugiat quis mauris ac, malesuada viverra est.
+
+        Phasellus bibendum faucibus velit, ac scelerisque velit tincidunt eu. Curabitur quis
+        suscipit erat, ac feugiat odio. Nullam et sapien et nibh maximus posuere. Vivamus faucibus
+        justo eget dictum sollicitudin. Etiam at leo eget elit facilisis lobortis. Maecenas
+        bibendum tortor non erat pretium dignissim. Fusce id imperdiet augue. Suspendisse dignissim
+        magna vel odio viverra varius. Maecenas suscipit ante et lorem sodales vehicula. Quisque
+        vel magna id sem suscipit iaculis. Etiam in elementum risus.
+
+        Suspendisse odio nisi, pharetra et purus sit amet, placerat lobortis diam. Phasellus enim
+        nunc, posuere sed porta in, ornare eu ipsum. Phasellus imperdiet porta neque, vitae dapibus
+        tellus feugiat eget. Nulla sodales leo ac efficitur luctus. Vivamus eget ipsum quis ante
+        pulvinar blandit. Vestibulum a justo convallis justo elementum viverra ut sit amet nisl.
+        Suspendisse eget augue fermentum, sagittis risus a, rhoncus elit. Vestibulum in maximus
+        tortor, non vestibulum libero. Nunc accumsan neque a nisl dapibus, id laoreet neque congue.
+        Pellentesque sapien odio, fringilla non nulla nec, varius placerat diam. Aliquam
+        consectetur neque eu ipsum posuere, nec dignissim sem faucibus. Donec sit amet tempor
+        sapien. Nam at libero vel lorem dapibus ultrices a vel augue. Nunc facilisis justo ante,
+        mollis tristique velit aliquet quis. Mauris consectetur odio at urna bibendum aliquam.
+
+           Nullam lectus orci, hendrerit ut ultrices in, dapibus pellentesque nibh. Aliquam arcu
+        metus, lobortis vel dignissim id, tempus ut ante. Integer vitae ante augue. In hac habitasse
+        platea dictumst. Vestibulum in tellus ante. Cras nisi tellus, congue ac velit quis, rhoncus
+        ornare ligula. Sed facilisis gravida pellentesque. Praesent id ultrices orci, ac ultricies
+        arcu. Donec at ante quis augue faucibus congue. Donec mattis quam dui, ut vestibulum orci
+        tempor mattis. Phasellus in quam id tortor varius ullamcorper ac ac ante. Proin cursus
+        accumsan sem, vel finibus lectus eleifend ut. Donec efficitur feugiat diam id ultricies. In
+        quis euismod nisi. Vestibulum eget viverra sapien. Donec scelerisque nec elit vel viverra.
+
+           Sed mi urna, rutrum quis augue vel, aliquet placerat diam. Proin faucibus in odio et
+        consequat. Proin ut ex in mauris eleifend efficitur. Praesent ullamcorper sollicitudin urna,
+        sed mollis elit hendrerit non. Duis leo lorem, efficitur eu auctor sit amet, scelerisque
+        scelerisque magna. Mauris eget massa auctor, viverra arcu a, elementum nibh. Sed
+        pellentesque, nulla sed condimentum posuere, tortor metus congue sem, nec placerat neque
+        magna vitae purus.
+
+           Etiam at risus vitae sapien aliquam condimentum. Vestibulum id libero placerat purus
+        vehicula consectetur. Pellentesque sapien sapien, posuere at pulvinar at, ultrices sed est.
+        Maecenas nec condimentum ante. Aenean volutpat, ex condimentum hendrerit hendrerit, quam
+        nisl  pharetra nibh, vitae bibendum nisl odio vel lacus. Morbi mi tellus, bibendum id mauris
+        eu,  facilisis volutpat turpis. Maecenas rutrum convallis felis. Quisque eget feugiat felis.
+        Duis pellentesque iaculis massa ut facilisis. Donec nec commodo magna. Integer aliquet orci
+        a odio eleifend elementum. Quisque molestie, urna ut molestie eleifend, odio neque maximus
+        enim, eget viverra metus lectus eget quam. Fusce nec urna ac neque bibendum aliquam vel quis
+        dui. Fusce ac quam consequat, feugiat leo vitae, auctor felis.
+
+           Sed ac metus mauris. Sed sed velit ut tortor aliquam vestibulum at eu arcu. Etiam eu
+        posuere lacus. Maecenas id lacus quis sem mollis sodales. Quisque justo sapien, vulputate ac
+        mi ut, congue vestibulum orci. Donec euismod erat rutrum, laoreet urna sed, accumsan purus.
+        Donec eu quam a sapien condimentum accumsan. Sed at tellus lorem. Curabitur bibendum, arcu
+        sit amet finibus sodales, mi sem finibus sem, eget scelerisque tellus neque vel urna.
+        Suspendisse eu augue nec erat suscipit luctus sed non metus.
+
+           Suspendisse porttitor ex ipsum. Pellentesque tristique eros sed pharetra porttitor.
+        Quisque ut elit vehicula, aliquet est nec, faucibus tellus. Donec ex augue, congue eu
+        dignissim  maximus, vestibulum at purus. Nulla quam enim, laoreet sit amet molestie vel,
+        dapibus nec tortor. Sed interdum massa ac orci gravida, vel viverra lacus lacinia. Donec
+        nisl lacus, fermentum at faucibus ac, consequat ut nibh. Praesent laoreet est augue, vitae
+        maximus dui efficitur sit amet. Cras ipsum tellus, tincidunt at volutpat non, tincidunt ut
+        elit. Morbi commodo sagittis gravida. Pellentesque sed ante massa. Phasellus a turpis non
+        turpis cursus consequat sed nec tortor. Proin et augue elit.
+
+           Duis finibus sem commodo rutrum pulvinar. In sollicitudin ante magna, vel facilisis
+        tellus fringilla vel. Nam purus ex, tincidunt eget varius at, euismod nec elit. Curabitur
+        consequat nulla vel nisi iaculis, ut mattis odio congue. Nulla et mollis tortor, a maximus
+        justo. Donec semper eros sed nunc rhoncus condimentum. Donec nibh purus, interdum non lectus
+        id, porta convallis eros.
+
+           Sed hendrerit, dui non sagittis sollicitudin, enim ex imperdiet sapien, et maximus lorem
+        dolor a enim. Nulla risus nisl, vestibulum at ornare posuere, congue in felis. Duis sagittis
+        id diam a varius. Donec viverra eu orci sodales commodo. Cras metus tortor, sodales vitae
+        auctor non, scelerisque a ante. Quisque sodales nisi libero, ut lobortis enim suscipit ut.
+        Cras mi ipsum, maximus non bibendum sit amet, pharetra quis ipsum. Vestibulum venenatis, odi
+        at hendrerit pretium, tellus diam auctor justo, non posuere quam mauris id nisl. Nam dolor
+        nibh, molestie et lectus et, scelerisque porta elit. Vestibulum viverra condimentum auctor.
+        In eros tortor, convallis sed quam eu, ultrices malesuada purus. Integer lorem quam,
+        ultricies at est consectetur, sagittis porttitor eros. Proin non risus vitae lacus
+        consectetur malesuada non pulvinar justo. Aliquam mollis nisi nunc, sit amet vulputate metus
+        sollicitudin vel.
+
+           Quisque auctor varius fermentum. Praesent mollis justo sit amet est consectetur, in
+        volutpat tellus mollis. Aenean at bibendum eros, at finibus nibh. Phasellus nec mi sem.
+        Mauris pellentesque dui sit amet lobortis aliquam. Ut nec massa at urna aliquam gravida vel
+        in magna. Donec consectetur sapien magna, a auctor sapien placerat eu.
+
+           Pellentesque aliquet ante sed lacus gravida rutrum. Maecenas euismod varius felis, nec
+        tempus metus tempus et. Nam convallis augue a massa scelerisque, vel pharetra dolor
+        scelerisque. Fusce porttitor mi a magna rutrum condimentum. Aliquam fermentum at turpis at
+        auctor. Nulla ut suscipit dui. Donec rutrum viverra aliquam. Donec elementum nisl sapien, ac
+        blandit risus porta facilisis. Proin tellus dolor, ornare vel magna sit amet, maximus
+        volutpat felis. Aenean euismod aliquet purus, at finibus nunc elementum eu. Ut faucibus
+        ullamcorper consectetur. Aenean egestas arcu id mauris elementum, at sollicitudin est
+        congue. Sed a odio mattis, sollicitudin erat ut, tristique dolor. Aliquam luctus risus quis
+        tellus semper, a vestibulum nulla viverra.
+
+           Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
+        himenaeos. Vestibulum sit amet nisi felis. Praesent condimentum consequat lacus pulvinar
+        imperdiet. Praesent vel condimentum quam. Maecenas eu aliquet odio. Vestibulum sed nulla
+        mattis lacus porta bibendum a ac eros. Nunc porttitor sagittis laoreet. Duis porta eros at
+        congue tristique. Pellentesque quis fringilla neque, a hendrerit tellus. Pellentesque ac
+        nibh ac tellus pulvinar porttitor et in est. Integer blandit lorem libero, eu pulvinar
+        tellus posuere eget. Vivamus pretium nulla ligula, ut dapibus massa fringilla in.
+        Suspendisse consectetur sem non elit porta, id pellentesque erat dapibus. Quisque ex mi,
+        tempus et hendrerit nec, gravida quis odio. Ut at mi in leo scelerisque venenatis.
+
+           Ut sed tellus in risus tincidunt tempor ut at arcu. Maecenas ut convallis justo. In
+        rutrum urna eu massa rhoncus, eget condimentum augue vehicula. Nullam eget placerat erat.
+        Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+        Aenean at volutpat orci, a lobortis dolor. Sed consequat facilisis interdum. Fusce libero
+        neque, fringilla in congue a, vehicula rutrum ipsum. Nam ornare placerat vestibulum. Proin
+        nec orci velit. Pellentesque scelerisque gravida diam, ut tristique libero tempus eu. Nam
+        semper lacus nec nulla volutpat imperdiet non eget tortor. Sed et pellentesque ligula.
+
+           Aenean a dolor dolor. Curabitur ut placerat lacus, sit amet aliquet orci. Aliquam erat
+        volutpat. Cras mollis sit amet lectus ornare pretium. Vestibulum fringilla orci vel est
+        iaculis, at mattis quam condimentum. Vivamus semper elit consectetur lectus gravida, in
+        sollicitudin mi fringilla. Donec eget lorem in orci blandit aliquam. Pellentesque libero
+        tellus, dignissim id augue et, vulputate viverra nisl. Cum sociis natoque penatibus et
+        magnis dis parturient montes, nascetur ridiculus mus. Donec ac vulputate metus, eu suscipit
+        sem. Donec placerat, nulla at sodales hendrerit, orci tortor vestibulum purus, non pharetra
+        nulla purus posuere arcu.
+
+           Quisque feugiat elit sem, ac interdum diam pharetra nec. Curabitur sem libero, vulputate
+        eu libero vitae, volutpat facilisis ligula. Aenean maximus erat laoreet, interdum ante in,
+        ultrices nisi. Nullam nec efficitur sapien. Integer feugiat et tortor ac bibendum. Donec a
+        scelerisque tortor. Cras quis viverra diam, vitae viverra ipsum. Aliquam ultrices neque sem,
+        congue sodales elit tempus sit amet. Pellentesque habitant morbi tristique senectus et netus
+        et malesuada fames ac turpis egestas. Sed dignissim ipsum eget diam rhoncus, ut finibus
+        nulla pretium. Morbi suscipit nibh vel nisl posuere molestie. Maecenas posuere turpis et
+        rutrum consectetur. Morbi venenatis arcu non gravida vulputate. Vivamus auctor tellus
+        ullamcorper ligula vestibulum cursus. Nunc gravida sit amet nisl quis facilisis.
+
+           Praesent ut justo vestibulum, accumsan mi et, feugiat purus. Nullam pulvinar iaculis
+        pharetra. Aliquam pulvinar risus sit amet elit suscipit tincidunt. In hac habitasse platea
+        dictumst. Etiam eget velit ac magna lacinia efficitur. Vestibulum ante ipsum primis in
+        faucibus orci luctus et ultrices posuere cubilia Curae; Cras volutpat tempus sollicitudin.
+        Ut et ante elit.
+
+           Sed ac tortor justo. Fusce sed metus libero. Duis sagittis tortor ac ante sollicitudin,
+        nec efficitur nibh euismod. Donec porttitor cursus quam, in aliquam lorem feugiat ut.
+        Aliquam tempor lacus eu feugiat feugiat. Nunc pulvinar, libero at auctor commodo, metus diam
+        commodo lorem, in feugiat elit ex non ligula. Quisque at vestibulum sapien, nec facilisis
+        neque. Aenean luctus, arcu ut rhoncus luctus, est massa rhoncus mauris, nec luctus urna sem
+        quis massa. Nam elit felis, congue et ligula eget, ultricies tincidunt erat. Vivamus
+        eleifend no dui ac dictum. In nulla justo, pulvinar ut tristique sed, congue et orci.
+
+           Quisque imperdiet mi lectus, ac scelerisque augue posuere ut. Duis non pulvinar ipsum,
+        finibus risus. Donec ullamcorper nisl at sodales lobortis. Mauris neque leo, vestibulum sit
+        amet placerat vel, aliquet vel sapien. Morbi massa tellus, scelerisque quis nisl in, feugiat
+        posuere augue. Aenean congue sem ut ipsum vulputate rhoncus vitae at eros. Maecenas in velit
+        orci pellentesque lobortis ac at felis. Vestibulum odio quam, lacinia dapibus ornare eu,
+        vulputate a eros. Curabitur eleifend ornare tellus, non sollicitudin justo viverra in. Sed
+        sodales neque et lacus semper, in pharetra est consequat. Nunc vehicula volutpat lectus, sit
+        amet scelerisque nisi. Aenean sollicitudin, sem at ultricies efficitur, eros metus
+        nisl, et fringilla felis lacus non orci. Praesent eros libero, finibus in purus id,
+        tempor ipsum. Donec suscipit libero velit. Aliquam quis diam pharetra, cursus ipsum in,
+        gravida metus. Maecenas ultrices ligula a ullamcorper scelerisque.
+
+           Donec tincidunt felis turpis, id venenatis neque convallis in. Proin euismod ligula nec
+        urna vulputate, sed elementum mauris ultrices. Ut efficitur sem vel mi vestibulum placerat.
+        Sed fermentum lacus nec metus dictum, a commodo quam fermentum. Sed vel vulputate magna.
+        Integer convallis nisi sit amet mi lobortis pellentesque. In egestas porttitor elit eu
+        scelerisque. Suspendisse eleifend vel enim quis tincidunt. Sed placerat risus et pretium
+        porttitor. Nam justo mi, cursus eu pellentesque vel, bibendum ut nisl. Nulla condimentum
+        lorem, non sagittis lorem volutpat vitae. Mauris nec libero lorem. Vestibulum lacus ex,
+        vulputate non massa vitae, pellentesque vestibulum dolor.
+
+           Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Suspendisse vitae erat nisl. Vestibulum elit ante, semper et semper sit amet, fringilla
+        sapien. Morbi ac nisi sit amet turpis tincidunt mattis ac eget nisl. Nunc a venenatis quam,
+        facilisis maximus odio. Aliquam erat volutpat. Maecenas leo enim, ornare a magna quis,
+        venenatis ornare nulla. Aliquam venenatis nibh et elit tincidunt, ut egestas lorem finibus.
+        Integer iaculis dolor sed enim blandit vestibulum. Nullam vel libero ultricies, sagittis
+        tortor non, molestie eros.</string>
+</resources>
diff --git a/tests/tests/assist/service/AndroidManifest.xml b/tests/tests/assist/service/AndroidManifest.xml
index cdbeef0..354d771 100644
--- a/tests/tests/assist/service/AndroidManifest.xml
+++ b/tests/tests/assist/service/AndroidManifest.xml
@@ -18,6 +18,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.assist.service">
 
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
     <application>
       <uses-library android:name="android.test.runner" />
       <service android:name=".MainInteractionService"
@@ -31,14 +33,7 @@
               <action android:name="android.service.voice.VoiceInteractionService" />
           </intent-filter>
       </service>
-      <activity android:name=".AssistStructureActivity" >
-          <intent-filter>
-              <action android:name="android.intent.action.START_TEST_ASSIST_STRUCTURE" />
-              <category android:name="android.intent.category.DEFAULT" />
-          </intent-filter>
-      </activity>
-      <activity android:name=".DisableContextActivity"
-                android:label="Disabled Context Activity">
+      <activity android:name=".DisableContextActivity" >
           <intent-filter>
               <action android:name="android.intent.action.START_TEST_DISABLE_CONTEXT" />
               <category android:name="android.intent.category.DEFAULT" />
@@ -47,8 +42,13 @@
       <activity android:name=".DelayedAssistantActivity"
                 android:label="Delay Assistant Start Activity">
           <intent-filter>
+              <action android:name="android.intent.action.START_TEST_ASSIST_STRUCTURE" />
               <action android:name="android.intent.action.START_TEST_LIFECYCLE" />
               <action android:name="android.intent.action.START_TEST_FLAG_SECURE" />
+              <action android:name="android.intent.action.START_TEST_SCREENSHOT" />
+              <action android:name="android.intent.action.START_TEST_EXTRA_ASSIST" />
+              <action android:name="android.intent.action.START_TEST_TEXTVIEW" />
+              <action android:name="android.intent.action.START_TEST_LARGE_VIEW_HIERARCHY" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
       </activity>
diff --git a/tests/tests/assist/service/res/layout/assist_layer.xml b/tests/tests/assist/service/res/layout/assist_layer.xml
index 49f35c9..3677208 100644
--- a/tests/tests/assist/service/res/layout/assist_layer.xml
+++ b/tests/tests/assist/service/res/layout/assist_layer.xml
@@ -1,9 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/assist_layer"
-        android:layout_width="match_parent"
-        android:background="@color/assist_layer_background"
-        android:layout_height="match_parent">
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/assist_layer"
+    android:layout_width="match_parent"
+    android:background="@color/assist_layer_background"
+    android:layout_height="match_parent">
     <TextView
         android:layout_centerInParent="true"
         android:text="@string/test_assistant_text"
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java
deleted file mode 100644
index 784d63b..0000000
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java
+++ /dev/null
@@ -1,42 +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 android.assist.service;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.util.Log;
-
-public class AssistStructureActivity extends Activity {
-    static final String TAG = "VoiceInteractionMain";
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        Intent intent = new Intent();
-        intent.setComponent(new ComponentName(this, MainInteractionService.class));
-        Log.i(TAG, "Starting service.");
-        finish();
-        startService(intent);
-    }
-}
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
index 31d1694..ddf43bd 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
@@ -24,13 +24,19 @@
 import android.util.Log;
 
 public class DelayedAssistantActivity extends Activity {
-    static final String TAG = "DelatyedAssistantActivity";
+    static final String TAG = "DelayedAssistantActivity";
 
+    @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         Intent intent = new Intent();
         intent.setComponent(new ComponentName(this, MainInteractionService.class));
         intent.putExtra(Utils.EXTRA_REGISTER_RECEIVER, true);
+        intent.putExtra(Utils.TESTCASE_TYPE, getIntent().getStringExtra(Utils.TESTCASE_TYPE));
+        intent.putExtra(Utils.DISPLAY_WIDTH_KEY,
+                getIntent().getIntExtra(Utils.DISPLAY_WIDTH_KEY, 0));
+        intent.putExtra(Utils.DISPLAY_HEIGHT_KEY,
+                getIntent().getIntExtra(Utils.DISPLAY_HEIGHT_KEY, 0));
         finish();
         ComponentName serviceName = startService(intent);
         Log.i(TAG, "Started service: " + serviceName);
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
index 52ba7ac..2fd540b 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
@@ -22,9 +22,6 @@
 import android.os.Bundle;
 import android.util.Log;
 
-/**
- * TODO(awlee): Change context on/off settings and test
- */
 public class DisableContextActivity extends Activity {
     static final String TAG = "DisableContextActivity";
 
@@ -38,8 +35,8 @@
         super.onStart();
         Intent intent = new Intent();
         intent.setComponent(new ComponentName(this, MainInteractionService.class));
+        Log.i(TAG, "Starting service.");
         finish();
-        ComponentName serviceName = startService(intent);
-        Log.i(TAG, "Started service: " + serviceName);
+        startService(intent);
     }
 }
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
index 7530933..916d676 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
@@ -30,11 +30,17 @@
 import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
 
+import java.lang.Exception;
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 public class MainInteractionService extends VoiceInteractionService {
     static final String TAG = "MainInteractionService";
     private Intent mIntent;
     private boolean mReady = false;
-    private BroadcastReceiver mBroadcastReceiver;
+    private BroadcastReceiver mBroadcastReceiver, mResumeReceiver;
+    private CountDownLatch mResumeLatch;
 
     @Override
     public void onReady() {
@@ -51,27 +57,37 @@
     }
 
     private void maybeStart() {
-       if (mIntent == null || !mReady) {
+        if (mIntent == null || !mReady) {
             Log.wtf(TAG, "Can't start session because either intent is null or onReady() "
                     + "has not been called yet. mIntent = " + mIntent + ", mReady = " + mReady);
         } else {
             if (isActiveService(this, new ComponentName(this, getClass()))) {
                 if (mIntent.getBooleanExtra(Utils.EXTRA_REGISTER_RECEIVER, false)) {
-                    Log.i(TAG, "Registering receiver to start session later");
+                    mResumeLatch = new CountDownLatch(1);
                     if (mBroadcastReceiver == null) {
                         mBroadcastReceiver = new MainInteractionServiceBroadcastReceiver();
-                        registerReceiver(mBroadcastReceiver, 
-                                new IntentFilter(Utils.BROADCAST_INTENT_START_ASSIST));
+                        IntentFilter filter = new IntentFilter();
+                        filter.addAction(Utils.BROADCAST_INTENT_START_ASSIST);
+                        registerReceiver(mBroadcastReceiver, filter);
+                        Log.i(TAG, "Registered receiver to start session later");
+
+                        IntentFilter resumeFilter = new IntentFilter(Utils.APP_3P_HASRESUMED);
+                        mResumeReceiver = new MainServiceAppResumeReceiver();
+                        registerReceiver(mResumeReceiver, resumeFilter);
+                        Log.i(TAG, "Registered receiver for resuming activity");
                     }
                     sendBroadcast(new Intent(Utils.ASSIST_RECEIVER_REGISTERED));
-                } else {
-                    Log.i(TAG, "Yay! about to start session");
-                    showSession(new Bundle(), VoiceInteractionSession.SHOW_WITH_ASSIST |
-                                VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
-                }
+              } else {
+                  Log.i(TAG, "Yay! about to start session");
+                  Bundle bundle = new Bundle();
+                  bundle.putString(Utils.TESTCASE_TYPE,
+                          mIntent.getStringExtra(Utils.TESTCASE_TYPE));
+                  showSession(bundle, VoiceInteractionSession.SHOW_WITH_ASSIST |
+                      VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
+              }
             } else {
                 Log.wtf(TAG, "**** Not starting MainInteractionService because" +
-                    " it is not set as the current voice interaction service");
+                        " it is not set as the current voice interaction service");
             }
         }
     }
@@ -79,8 +95,51 @@
     private class MainInteractionServiceBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
+            Log.i(MainInteractionService.TAG, "Recieved broadcast to start session now.");
             if (intent.getAction().equals(Utils.BROADCAST_INTENT_START_ASSIST)) {
-                showSession(new Bundle(), SHOW_WITH_ASSIST | SHOW_WITH_SCREENSHOT);
+                String testCaseName = intent.getStringExtra(Utils.TESTCASE_TYPE);
+                Log.i(MainInteractionService.TAG, "trying to start 3p activity: " + testCaseName);
+                Bundle extras = intent.getExtras();
+                if (extras == null) {
+                    extras = new Bundle();
+                }
+                if (testCaseName.equals(Utils.SCREENSHOT)) {
+                    try {
+                        // extra info to pass along to 3p activity.
+
+                        Intent intent3p = new Intent();
+                        Log.i(TAG, "starting the 3p app again");
+                        intent3p.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        intent3p.setAction("android.intent.action.TEST_APP_" + testCaseName);
+                        intent3p.setComponent(Utils.getTestAppComponent(testCaseName));
+                        intent3p.putExtras(extras);
+                        startActivity(intent3p);
+                        if (!MainInteractionService.this.mResumeLatch
+                                .await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                            Log.i(TAG, "waited for 3p activity to resume");
+                        }
+                    } catch (Exception e) {
+                        Log.i(TAG, "failed so reload 3p app: " + e.toString());
+                    }
+                }
+                extras.putString(Utils.TESTCASE_TYPE, mIntent.getStringExtra(Utils.TESTCASE_TYPE));
+                MainInteractionService.this.showSession(
+                        extras, SHOW_WITH_ASSIST | SHOW_WITH_SCREENSHOT);
+            }
+        }
+    }
+
+    private class MainServiceAppResumeReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Utils.APP_3P_HASRESUMED)) {
+                Log.i(MainInteractionService.TAG,
+                    "3p activity has resumed in this new receiver");
+                if (MainInteractionService.this.mResumeLatch != null) {
+                    MainInteractionService.this.mResumeLatch.countDown();
+                } else {
+                    Log.i(TAG, "mResumeLatch was null");
+                }
             }
         }
     }
@@ -91,4 +150,4 @@
             unregisterReceiver(mBroadcastReceiver);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
index f297b3e..7bca9be 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -24,15 +24,24 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Bitmap;
+import android.graphics.Color;
+
+import android.graphics.Point;
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionSession;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.Display;
+import android.view.ViewTreeObserver;
 
 import java.io.ByteArrayOutputStream;
+import java.util.Date;
 
 import android.assist.common.Utils;
+import android.view.WindowManager;
 
 public class MainInteractionSession extends VoiceInteractionSession {
     static final String TAG = "MainInteractionSession";
@@ -43,7 +52,13 @@
 
     private boolean hasReceivedAssistData = false;
     private boolean hasReceivedScreenshot = false;
+    private int mCurColor;
+    private int mDisplayHeight;
+    private int mDisplayWidth;
+    private Bitmap mScreenshot;
     private BroadcastReceiver mReceiver;
+    private String mTestName;
+    private View mContentView;
 
     MainInteractionSession(Context context) {
         super(context);
@@ -78,13 +93,30 @@
 
     @Override
     public void onShow(Bundle args, int showFlags) {
-        // set some content view.
-        // TODO: check that the view takes up the whole screen.
-        // check that interactor mode is for assist
         if ((showFlags & SHOW_WITH_ASSIST) == 0) {
             return;
         }
+        mTestName = args.getString(Utils.TESTCASE_TYPE, "");
+        mCurColor = args.getInt(Utils.SCREENSHOT_COLOR_KEY);
+        mDisplayHeight = args.getInt(Utils.DISPLAY_HEIGHT_KEY);
+        mDisplayWidth = args.getInt(Utils.DISPLAY_WIDTH_KEY);
         super.onShow(args, showFlags);
+        mContentView.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    mContentView.getViewTreeObserver().removeOnPreDrawListener(this);
+                    Display d = mContentView.getDisplay();
+                    Point displayPoint = new Point();
+                    d.getRealSize(displayPoint);
+                    Intent intent = new Intent(Utils.BROADCAST_CONTENT_VIEW_HEIGHT);
+                    intent.putExtra(Utils.EXTRA_CONTENT_VIEW_HEIGHT, mContentView.getHeight());
+                    intent.putExtra(Utils.EXTRA_CONTENT_VIEW_WIDTH, mContentView.getWidth());
+                    intent.putExtra(Utils.EXTRA_DISPLAY_POINT, displayPoint);
+                    mContext.sendBroadcast(intent);
+                    return true;
+                }
+            });
     }
 
     @Override
@@ -92,7 +124,7 @@
         /*@Nullable*/ AssistContent content) {
         Log.i(TAG, "onHandleAssist");
         Log.i(TAG,
-            String.format("Bundle: %s, Structure: %s, Content: %s", data, structure, content));
+                String.format("Bundle: %s, Structure: %s, Content: %s", data, structure, content));
         super.onHandleAssist(data, structure, content);
 
         // send to test to verify that this is accurate.
@@ -107,17 +139,52 @@
     public void onHandleScreenshot(/*@Nullable*/ Bitmap screenshot) {
         Log.i(TAG, String.format("onHandleScreenshot - Screenshot: %s", screenshot));
         super.onHandleScreenshot(screenshot);
-        ByteArrayOutputStream bs = new ByteArrayOutputStream();
+
         if (screenshot != null) {
-            screenshot.compress(Bitmap.CompressFormat.PNG, 50, bs);
-            mAssistData.putByteArray(Utils.ASSIST_SCREENSHOT_KEY, bs.toByteArray());
+            mAssistData.putBoolean(Utils.ASSIST_SCREENSHOT_KEY, true);
+
+            if (mTestName.equals(Utils.SCREENSHOT)) {
+                boolean screenshotMatches = compareScreenshot(screenshot, mCurColor);
+                Log.i(TAG, "this is a screenshot test. Matches? " + screenshotMatches);
+                mAssistData.putBoolean(
+                    Utils.COMPARE_SCREENSHOT_KEY, screenshotMatches);
+            }
         } else {
-            mAssistData.putByteArray(Utils.ASSIST_SCREENSHOT_KEY, null);
+            mAssistData.putBoolean(Utils.ASSIST_SCREENSHOT_KEY, false);
         }
         hasReceivedScreenshot = true;
         maybeBroadcastResults();
     }
 
+    private boolean compareScreenshot(Bitmap screenshot, int color) {
+        Point size = new Point(mDisplayWidth, mDisplayHeight);
+
+        if (screenshot.getWidth() != size.x || screenshot.getHeight() != size.y) {
+            Log.i(TAG, "width  or height didn't match: " + size + " vs " + screenshot.getWidth()
+                    + "," + screenshot.getHeight());
+            return false;
+        }
+        int[] pixels = new int[size.x * size.y];
+        screenshot.getPixels(pixels, 0, size.x, 0, 0, size.x, size.y);
+
+        int expectedColor = 0;
+        int wrongColor = 0;
+        for (int pixel : pixels) {
+            if (pixel == color) {
+                expectedColor += 1;
+            } else {
+                wrongColor += 1;
+            }
+        }
+
+        double colorRatio = (double) expectedColor / (expectedColor + wrongColor);
+        Log.i(TAG, "the ratio is " + colorRatio);
+        if (colorRatio < 0.6) {
+            return false;
+        }
+        return true;
+    }
+
     private void maybeBroadcastResults() {
         if (!hasReceivedAssistData) {
             Log.i(TAG, "waiting for assist data before broadcasting results");
@@ -141,13 +208,7 @@
         if (f == null) {
             Log.wtf(TAG, "layout inflater was null");
         }
-        return f.inflate(R.layout.assist_layer,null);
-    }
-
-    class DoneReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.i(TAG, "Done_broadcast " + intent.getAction());
-        }
+        mContentView = f.inflate(R.layout.assist_layer,null);
+        return mContentView;
     }
 }
diff --git a/tests/tests/assist/src/android/assist/TestStartActivity.java b/tests/tests/assist/src/android/assist/TestStartActivity.java
deleted file mode 100644
index df9b534..0000000
--- a/tests/tests/assist/src/android/assist/TestStartActivity.java
+++ /dev/null
@@ -1,84 +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 android.assist;
-
-import android.assist.common.Utils;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestStartActivity extends Activity {
-    static final String TAG = "TestStartActivity";
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.i(TAG, " in onCreate");
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log.i(TAG, " in onResume");
-    }
-
-    public void startTest(String testCaseName) {
-        Log.i(TAG, "Starting test activity for TestCaseType = " + testCaseName);
-        Intent intent = new Intent();
-        intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
-        intent.setAction("android.intent.action.START_TEST_" + testCaseName);
-        intent.setComponent(new ComponentName("android.assist.service",
-                "android.assist." + Utils.getTestActivity(testCaseName)));
-        startActivity(intent);
-    }
-
-    public void start3pApp(String testCaseName) {
-        Intent intent = new Intent();
-        intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
-        intent.setComponent(Utils.getTestAppComponent(testCaseName));
-        startActivity(intent);
-    }
-
-    @Override protected void onPause() {
-        Log.i(TAG, " in onPause");
-        super.onPause();
-    }
-
-    @Override protected void onStart() {
-        super.onStart();
-        Log.i(TAG, " in onStart");
-    }
-
-    @Override protected void onRestart() {
-        super.onRestart();
-        Log.i(TAG, " in onRestart");
-    }
-
-    @Override protected void onStop() {
-        Log.i(TAG, " in onStop");
-        super.onStop();
-    }
-
-    @Override
-    protected void onDestroy() {
-        Log.i(TAG, " in onDestroy");
-        super.onDestroy();
-    }
-}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
index 763ecef..4c1b1f3 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -18,7 +18,15 @@
 
 import android.assist.common.Utils;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 
 /**
@@ -26,10 +34,13 @@
  */
 
 public class AssistStructureTest extends AssistTestBase {
-    static final String TAG = "AssistStructureTest";
-
+    private static final String TAG = "AssistStructureTest";
     private static final String TEST_CASE_TYPE = Utils.ASSIST_STRUCTURE;
 
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
     public AssistStructureTest() {
         super();
     }
@@ -37,13 +48,63 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        setUpAndRegisterReceiver();
         startTestActivity(TEST_CASE_TYPE);
-        waitForBroadcast();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new AssistStructureTestBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
     }
 
     public void testAssistStructure() throws Exception {
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        waitForOnResume();
+        startSession();
+        waitForContext();
         verifyAssistDataNullness(false, false, false, false);
+
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
-                    false /*FLAG_SECURE set*/);
+                false /*FLAG_SECURE set*/);
     }
-}
\ No newline at end of file
+
+    private class AssistStructureTestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED)) {
+                if (mHasResumedLatch != null) {
+                    mHasResumedLatch.countDown();
+                }
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTest.java b/tests/tests/assist/src/android/assist/cts/AssistTest.java
deleted file mode 100644
index 5241c4e..0000000
--- a/tests/tests/assist/src/android/assist/cts/AssistTest.java
+++ /dev/null
@@ -1,46 +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 android.assist.cts;
-
-import android.assist.TestStartActivity;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-
-import junit.framework.Assert;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import android.assist.common.Utils;
-
-public class AssistTest extends ActivityInstrumentationTestCase2<TestStartActivity> {
-    static final String TAG = "AssistTest";
-    private static final int TIMEOUT_MS = 2 * 1000;
-
-    public AssistTest() {
-        super(TestStartActivity.class);
-    }
-}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index a7e7087..46fb8d9 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -16,40 +16,60 @@
 
 package android.assist.cts;
 
-import android.assist.TestStartActivity;
+import android.assist.cts.TestStartActivity;
 import android.assist.common.Utils;
 
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.cts.util.SystemUtil;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.webkit.WebView;
+import android.widget.EditText;
+import android.widget.TextView;
 
+import java.lang.Math;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class AssistTestBase extends ActivityInstrumentationTestCase2<TestStartActivity> {
-    static final String TAG = "AssistTestBase";
+    private static final String TAG = "AssistTestBase";
 
     protected TestStartActivity mTestActivity;
     protected AssistContent mAssistContent;
     protected AssistStructure mAssistStructure;
-    protected Bitmap mScreenshot;
+    protected boolean mScreenshot;
+    protected Bitmap mAppScreenshot;
     protected BroadcastReceiver mReceiver;
     protected Bundle mAssistBundle;
     protected Context mContext;
-    protected CountDownLatch mLatch, mAssistantReadyLatch;
+    protected CountDownLatch mLatch, mScreenshotLatch, mHasResumedLatch;
+    protected boolean mScreenshotMatches;
+    private Point mDisplaySize;
     private String mTestName;
+    private View mView;
 
     public AssistTestBase() {
         super(TestStartActivity.class);
@@ -58,29 +78,49 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mAssistantReadyLatch = new CountDownLatch(1);
         mContext = getInstrumentation().getTargetContext();
         SystemUtil.runShellCommand(getInstrumentation(),
-            "settings put secure assist_structure_enabled 1");
+                "settings put secure assist_structure_enabled 1");
         SystemUtil.runShellCommand(getInstrumentation(),
-            "settings put secure assist_screenshot_enabled 1");
+                "settings put secure assist_screenshot_enabled 1");
         logContextAndScreenshotSetting();
+
+        // reset old values
+        mScreenshotMatches = false;
+        mScreenshot = false;
+        mAssistStructure = null;
+        mAssistContent = null;
+        mAssistBundle = null;
+
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new TestResultsReceiver();
+        mContext.registerReceiver(mReceiver,
+            new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mContext.unregisterReceiver(mReceiver);
         mTestActivity.finish();
-        super.tearDown();
         mContext.sendBroadcast(new Intent(Utils.HIDE_SESSION));
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+        super.tearDown();
     }
 
+    /**
+     * Starts the shim service activity
+     */
     protected void startTestActivity(String testName) {
         Intent intent = new Intent();
         mTestName = testName;
         intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + testName);
         intent.setComponent(new ComponentName(getInstrumentation().getContext(),
-            TestStartActivity.class));
+                TestStartActivity.class));
+        intent.putExtra(Utils.TESTCASE_TYPE, testName);
         setActivityIntent(intent);
         mTestActivity = getActivity();
     }
@@ -88,9 +128,9 @@
     /**
      * Called when waiting for Assistant's Broadcast Receiver to be setup
      */
-    public void waitForAssistantToBeReady() throws Exception {
+    public void waitForAssistantToBeReady(CountDownLatch latch) throws Exception {
         Log.i(TAG, "waiting for assistant to be ready before continuing");
-        if (!mAssistantReadyLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+        if (!latch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
             fail("Assistant was not ready before timeout of: " + Utils.TIMEOUT_MS + "msec");
         }
     }
@@ -99,11 +139,33 @@
      * Send broadcast to MainInteractionService to start a session
      */
     protected void startSession() {
-        mContext.sendBroadcast(new Intent(Utils.BROADCAST_INTENT_START_ASSIST));
+        startSession(mTestName, new Bundle());
+    }
+
+    protected void startSession(String testName, Bundle extras) {
+        Intent intent = new Intent(Utils.BROADCAST_INTENT_START_ASSIST);
+        Log.i(TAG, "passed in class test name is: " + testName);
+        intent.putExtra(Utils.TESTCASE_TYPE, testName);
+        addDimensionsToIntent(intent);
+        intent.putExtras(extras);
+        mContext.sendBroadcast(intent);
     }
 
     /**
-     * Called after startTestActivity
+     * Calculate display dimensions (including navbar) to pass along in the given intent.
+     */
+    private void addDimensionsToIntent(Intent intent) {
+        if (mDisplaySize == null) {
+            Display display = mTestActivity.getWindowManager().getDefaultDisplay();
+            mDisplaySize = new Point();
+            display.getRealSize(mDisplaySize);
+        }
+        intent.putExtra(Utils.DISPLAY_WIDTH_KEY, mDisplaySize.x);
+        intent.putExtra(Utils.DISPLAY_HEIGHT_KEY, mDisplaySize.y);
+    }
+
+    /**
+     * Called after startTestActivity. Includes check for receiving context.
      */
     protected boolean waitForBroadcast() throws Exception {
         mTestActivity.start3pApp(mTestName);
@@ -113,22 +175,24 @@
 
     protected boolean waitForContext() throws Exception {
         mLatch = new CountDownLatch(1);
+
         if (mReceiver != null) {
             mContext.unregisterReceiver(mReceiver);
         }
         mReceiver = new TestResultsReceiver();
         mContext.registerReceiver(mReceiver,
-            new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
+                new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
 
-        if (!mLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Failed to receive broadcast in " + Utils.TIMEOUT_MS + "msec");
-            return false;
+        if (!mLatch.await(Utils.getAssistDataTimeout(mTestName), TimeUnit.MILLISECONDS)) {
+            fail("Fail to receive broadcast in " + Utils.getAssistDataTimeout(mTestName) + "msec");
         }
+        Log.i(TAG, "Received broadcast with all information.");
         return true;
     }
 
     /**
      * Checks that the nullness of values are what we expect.
+     *
      * @param isBundleNull True if assistBundle should be null.
      * @param isStructureNull True if assistStructure should be null.
      * @param isContentNull True if assistContent should be null.
@@ -139,27 +203,44 @@
 
         if ((mAssistContent == null) != isContentNull) {
             fail(String.format("Should %s have been null - AssistContent: %s",
-                    isContentNull? "":"not", mAssistContent));
+                    isContentNull ? "" : "not", mAssistContent));
         }
 
         if ((mAssistStructure == null) != isStructureNull) {
             fail(String.format("Should %s have been null - AssistStructure: %s",
-                isStructureNull ? "" : "not", mAssistStructure));
+                    isStructureNull ? "" : "not", mAssistStructure));
         }
 
         if ((mAssistBundle == null) != isBundleNull) {
             fail(String.format("Should %s have been null - AssistBundle: %s",
-                    isBundleNull? "":"not", mAssistBundle));
+                    isBundleNull ? "" : "not", mAssistBundle));
         }
 
-        if ((mScreenshot == null) != isScreenshotNull) {
+        if (mScreenshot == isScreenshotNull) {
             fail(String.format("Should %s have been null - Screenshot: %s",
-                    isScreenshotNull? "":"not", mScreenshot));
+                    isScreenshotNull ? "":"not", mScreenshot));
         }
     }
 
     /**
-     * Traverses and compares the view heirarchy of the backgroundApp and the view we expect.
+     * Sends a broadcast with the specified scroll positions to the test app.
+     */
+    protected void scrollTestApp(int scrollX, int scrollY, boolean scrollTextView,
+            boolean scrollScrollView) {
+        mTestActivity.scrollText(scrollX, scrollY, scrollTextView, scrollScrollView);
+        Intent intent = null;
+        if (scrollTextView) {
+            intent = new Intent(Utils.SCROLL_TEXTVIEW_ACTION);
+        } else if (scrollScrollView) {
+            intent = new Intent(Utils.SCROLL_SCROLLVIEW_ACTION);
+        }
+        intent.putExtra(Utils.SCROLL_X_POSITION, scrollX);
+        intent.putExtra(Utils.SCROLL_Y_POSITION, scrollY);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Verifies the view hierarchy of the backgroundApp matches the assist structure.
      *
      * @param backgroundApp ComponentName of app the assistant is invoked upon
      * @param isSecureWindow Denotes whether the activity has FLAG_SECURE set
@@ -167,28 +248,212 @@
     protected void verifyAssistStructure(ComponentName backgroundApp, boolean isSecureWindow) {
         // Check component name matches
         assertEquals(backgroundApp.flattenToString(),
-            mAssistStructure.getActivityComponent().flattenToString());
+                mAssistStructure.getActivityComponent().flattenToString());
 
-        int numWindows = mAssistStructure.getWindowNodeCount();
-        assertEquals(1, numWindows);
-        for (int i = 0; i < numWindows; i++) {
-            AssistStructure.ViewNode node = mAssistStructure.getWindowNodeAt(i).getRootViewNode();
-            // TODO: Actually traverse the view heirarchy and verify it matches what we expect
-            // If isSecureWindow, will not have any children.
-        }
+        Log.i(TAG, "Traversing down structure for: " + backgroundApp.flattenToString());
+        mView = mTestActivity.findViewById(android.R.id.content).getRootView();
+        verifyHierarchy(mAssistStructure, isSecureWindow);
     }
 
     protected void logContextAndScreenshotSetting() {
         Log.i(TAG, "Context is: " + Settings.Secure.getString(
-            mContext.getContentResolver(), "assist_structure_enabled"));
+                mContext.getContentResolver(), "assist_structure_enabled"));
         Log.i(TAG, "Screenshot is: " + Settings.Secure.getString(
-            mContext.getContentResolver(), "assist_screenshot_enabled"));
+                mContext.getContentResolver(), "assist_screenshot_enabled"));
+    }
+
+    /**
+     * Recursively traverse and compare properties in the View hierarchy with the Assist Structure.
+     */
+    public void verifyHierarchy(AssistStructure structure, boolean isSecureWindow) {
+        Log.i(TAG, "verifyHierarchy");
+        Window mWindow = mTestActivity.getWindow();
+
+        int numWindows = structure.getWindowNodeCount();
+        // TODO: multiple windows?
+        assertEquals("Number of windows don't match", 1, numWindows);
+
+        for (int i = 0; i < numWindows; i++) {
+            AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(i);
+            Log.i(TAG, "Title: " + windowNode.getTitle());
+            // verify top level window bounds are as big as the screen and pinned to 0,0
+            assertEquals("Window left position wrong: was " + windowNode.getLeft(),
+                    windowNode.getLeft(), 0);
+            assertEquals("Window top position wrong: was " + windowNode.getTop(),
+                    windowNode.getTop(), 0);
+
+            traverseViewAndStructure(
+                    mView,
+                    windowNode.getRootViewNode(),
+                    isSecureWindow);
+        }
+    }
+
+    private void traverseViewAndStructure(View parentView, ViewNode parentNode,
+            boolean isSecureWindow) {
+        ViewGroup parentGroup;
+
+        if (parentView == null && parentNode == null) {
+            Log.i(TAG, "Views are null, done traversing this branch.");
+            return;
+        } else if (parentNode == null || parentView == null) {
+            fail(String.format("Views don't match. View: %s, Node: %s", parentView, parentNode));
+        }
+
+        // Debugging
+        Log.i(TAG, "parentView is of type: " + parentView.getClass().getName());
+        if (parentView instanceof ViewGroup) {
+            for (int childInt = 0; childInt < ((ViewGroup) parentView).getChildCount();
+                    childInt++) {
+                Log.i(TAG,
+                        "viewchild" + childInt + " is of type: "
+                        + ((ViewGroup) parentView).getChildAt(childInt).getClass().getName());
+            }
+        }
+        String parentViewId = null;
+        if (parentView.getId() > 0) {
+            parentViewId = mTestActivity.getResources().getResourceEntryName(parentView.getId());
+            Log.i(TAG, "View ID: " + parentViewId);
+        }
+
+        Log.i(TAG, "parentNode is of type: " + parentNode.getClassName());
+        for (int nodeInt = 0; nodeInt < parentNode.getChildCount(); nodeInt++) {
+            Log.i(TAG,
+                    "nodechild" + nodeInt + " is of type: "
+                    + parentNode.getChildAt(nodeInt).getClassName());
+        }
+        Log.i(TAG, "Node ID: " + parentNode.getIdEntry());
+
+        assertEquals("IDs do not match", parentViewId, parentNode.getIdEntry());
+
+        int numViewChildren = 0;
+        int numNodeChildren = 0;
+        if (parentView instanceof ViewGroup) {
+            numViewChildren = ((ViewGroup) parentView).getChildCount();
+        }
+        numNodeChildren = parentNode.getChildCount();
+
+        if (isSecureWindow) {
+            assertTrue("ViewNode property isAssistBlocked is false", parentNode.isAssistBlocked());
+            assertEquals("Secure window should only traverse root node.", 0, numNodeChildren);
+            isSecureWindow = false;
+        } else if (parentNode.getClassName().equals("android.webkit.WebView")) {
+            // WebView will also appear to have no children while the node does, traverse node
+            assertTrue("AssistStructure returned a WebView where the view wasn't one",
+                    parentView instanceof WebView);
+
+            boolean textInWebView = false;
+
+            for (int i = numNodeChildren - 1; i >= 0; i--) {
+               textInWebView |= traverseWebViewForText(parentNode.getChildAt(i));
+            }
+            assertTrue("Did not find expected strings inside WebView", textInWebView);
+        } else {
+            assertEquals("Number of children did not match.", numViewChildren, numNodeChildren);
+
+            verifyViewProperties(parentView, parentNode);
+
+            if (parentView instanceof ViewGroup) {
+                parentGroup = (ViewGroup) parentView;
+
+                // TODO: set a max recursion level
+                for (int i = numNodeChildren - 1; i >= 0; i--) {
+                    View childView = parentGroup.getChildAt(i);
+                    ViewNode childNode = parentNode.getChildAt(i);
+
+                    // if isSecureWindow, should not have reached this point.
+                    assertFalse(isSecureWindow);
+                    traverseViewAndStructure(childView, childNode, isSecureWindow);
+                }
+            }
+        }
+    }
+
+    /** 
+     * Return true if the expected strings are found in the WebView, else fail.
+     */
+    private boolean traverseWebViewForText(ViewNode parentNode) {
+        boolean textFound = false;
+        if (parentNode.getText() != null 
+                && parentNode.getText().toString().equals(Utils.WEBVIEW_HTML_GREETING)) {
+            return true;
+        }
+        for (int i = parentNode.getChildCount() - 1; i >= 0; i--) {
+            textFound |= traverseWebViewForText(parentNode.getChildAt(i));
+        }
+        return textFound;
+    }
+
+    /**
+     * Compare view properties of the view hierarchy with that reported in the assist structure.
+     */
+    private void verifyViewProperties(View parentView, ViewNode parentNode) {
+        assertEquals("Left positions do not match.", parentView.getLeft(), parentNode.getLeft());
+        assertEquals("Top positions do not match.", parentView.getTop(), parentNode.getTop());
+
+        int viewId = parentView.getId();
+
+        if (viewId > 0) {
+            if (parentNode.getIdEntry() != null) {
+                assertEquals("View IDs do not match.",
+                        mTestActivity.getResources().getResourceEntryName(viewId),
+                        parentNode.getIdEntry());
+            }
+        } else {
+            assertNull("View Node should not have an ID.", parentNode.getIdEntry());
+        }
+
+        Log.i(TAG, "parent text: " + parentNode.getText());
+        if (parentView instanceof TextView) {
+            Log.i(TAG, "view text: " + ((TextView) parentView).getText());
+        }
+
+
+        assertEquals("Scroll X does not match.", parentView.getScrollX(), parentNode.getScrollX());
+        assertEquals("Scroll Y does not match.", parentView.getScrollY(), parentNode.getScrollY());
+        assertEquals("Heights do not match.", parentView.getHeight(), parentNode.getHeight());
+        assertEquals("Widths do not match.", parentView.getWidth(), parentNode.getWidth());
+
+        if (parentView instanceof TextView) {
+            if (parentView instanceof EditText) {
+                assertEquals("Text selection start does not match",
+                    ((EditText)parentView).getSelectionStart(), parentNode.getTextSelectionStart());
+                assertEquals("Text selection end does not match",
+                        ((EditText)parentView).getSelectionEnd(), parentNode.getTextSelectionEnd());
+            }
+            TextView textView = (TextView) parentView;
+            assertEquals(textView.getTextSize(), parentNode.getTextSize());
+            String viewString = textView.getText().toString();
+            String nodeString = parentNode.getText().toString();
+
+            if (parentNode.getScrollX() == 0 && parentNode.getScrollY() == 0) {
+                Log.i(TAG, "Verifying text within TextView at the beginning");
+                Log.i(TAG, "view string: " + viewString);
+                Log.i(TAG, "node string: " + nodeString);
+                assertTrue("String length is unexpected: original string - " + viewString.length() +
+                                ", string in AssistData - " + nodeString.length(),
+                        viewString.length() >= nodeString.length());
+                assertTrue("Expected a longer string to be shown. expected: "
+                                + Math.min(viewString.length(), 30) + " was: " + nodeString
+                                .length(),
+                        nodeString.length() >= Math.min(viewString.length(), 30));
+                for (int x = 0; x < parentNode.getText().length(); x++) {
+                    assertEquals("Char not equal at index: " + x,
+                            ((TextView) parentView).getText().toString().charAt(x),
+                            parentNode.getText().charAt(x));
+                }
+            } else if (parentNode.getScrollX() == parentView.getWidth()) {
+
+            }
+        } else {
+            assertNull(parentNode.getText());
+        }
     }
 
     class TestResultsReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equalsIgnoreCase(Utils.BROADCAST_ASSIST_DATA_INTENT)) { // not necessary?
+            if (intent.getAction().equalsIgnoreCase(Utils.BROADCAST_ASSIST_DATA_INTENT)) {
                 Log.i(TAG, "Received broadcast with assist data.");
                 Bundle assistData = intent.getExtras();
                 AssistTestBase.this.mAssistBundle = assistData.getBundle(Utils.ASSIST_BUNDLE_KEY);
@@ -197,17 +462,20 @@
                 AssistTestBase.this.mAssistContent = assistData.getParcelable(
                         Utils.ASSIST_CONTENT_KEY);
 
-                byte[] bitmapArray = assistData.getByteArray(Utils.ASSIST_SCREENSHOT_KEY);
-                if (bitmapArray != null) {
-                    AssistTestBase.this.mScreenshot = BitmapFactory.decodeByteArray(
-                            bitmapArray, 0, bitmapArray.length);
-                } else {
-                    AssistTestBase.this.mScreenshot = null;
-                }
+                AssistTestBase.this.mScreenshot =
+                        assistData.getBoolean(Utils.ASSIST_SCREENSHOT_KEY, false);
+
+                AssistTestBase.this.mScreenshotMatches = assistData.getBoolean(
+                        Utils.COMPARE_SCREENSHOT_KEY, false);
 
                 if (mLatch != null) {
+                    Log.i(AssistTestBase.TAG, "counting down latch. received assist data.");
                     mLatch.countDown();
                 }
+            } else if (intent.getAction().equals(Utils.APP_3P_HASRESUMED)) {
+                if (mHasResumedLatch != null) {
+                    mHasResumedLatch.countDown();
+                }
             }
         }
     }
diff --git a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
new file mode 100644
index 0000000..557134b
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.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 android.assist.cts;
+
+import android.assist.cts.TestStartActivity;
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.cts.util.SystemUtil;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Test verifying the Content View of the Assistant */
+public class AssistantContentViewTest extends AssistTestBase {
+    private static final String TAG = "ContentViewTest";
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mContentViewLatch, mReadyLatch;
+    private Intent mIntent;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mContentViewLatch = new CountDownLatch(1);
+        mReadyLatch = new CountDownLatch(1);
+        setUpAndRegisterReceiver();
+        startTestActivity(Utils.VERIFY_CONTENT_VIEW);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new AssistantContentViewReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.BROADCAST_CONTENT_VIEW_HEIGHT);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+
+    }
+
+    private void waitForContentView() throws Exception {
+        Log.i(TAG, "waiting for the Assistant's Content View  before continuing");
+        if (!mContentViewLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("failed to receive content view in " + Utils.TIMEOUT_MS + "msec");
+        }
+    }
+
+    public void testAssistantContentViewDimens() throws Exception {
+        mTestActivity.startTest(Utils.VERIFY_CONTENT_VIEW);
+        waitForAssistantToBeReady(mReadyLatch);
+        startSession();
+        waitForContentView();
+        int height = mIntent.getIntExtra(Utils.EXTRA_CONTENT_VIEW_HEIGHT, 0);
+        int width = mIntent.getIntExtra(Utils.EXTRA_CONTENT_VIEW_WIDTH, 0);
+        Point displayPoint = (Point) mIntent.getParcelableExtra(Utils.EXTRA_DISPLAY_POINT);
+        assertEquals(displayPoint.y, height);
+        assertEquals(displayPoint.x, width);
+    }
+
+    private class AssistantContentViewReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.BROADCAST_CONTENT_VIEW_HEIGHT)) {
+                mIntent = intent;
+                if (mContentViewLatch != null) {
+                    mContentViewLatch.countDown();
+                }
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
index 9b29407..5c8aa19 100644
--- a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
+++ b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
@@ -16,7 +16,6 @@
 
 package android.assist.cts;
 
-import android.assist.TestStartActivity;
 import android.assist.common.Utils;
 
 import android.app.Activity;
@@ -65,6 +64,7 @@
 
     public void testContextAndScreenshotOff() throws Exception {
         // Both settings off
+        Log.i(TAG, "DisableContext: Screenshot OFF, Context OFF");
         SystemUtil.runShellCommand(getInstrumentation(),
             "settings put secure assist_structure_enabled 0");
         SystemUtil.runShellCommand(getInstrumentation(),
@@ -76,6 +76,7 @@
         verifyAssistDataNullness(true, true, true, true);
 
         // Screenshot off, context on
+        Log.i(TAG, "DisableContext: Screenshot OFF, Context ON");
         SystemUtil.runShellCommand(getInstrumentation(),
             "settings put secure assist_structure_enabled 1");
         SystemUtil.runShellCommand(getInstrumentation(),
@@ -87,6 +88,7 @@
         verifyAssistDataNullness(false, false, false, true);
 
         // Context off, screenshot on
+        Log.i(TAG, "DisableContext: Screenshot ON, Context OFF");
         SystemUtil.runShellCommand(getInstrumentation(),
             "settings put secure assist_structure_enabled 0");
         SystemUtil.runShellCommand(getInstrumentation(),
diff --git a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
new file mode 100644
index 0000000..28b2af2
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.assist.cts;
+
+import android.assist.common.Utils;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ExtraAssistDataTest extends AssistTestBase {
+    private static final String TAG = "ExtraAssistDataTest";
+    private static final String TEST_CASE_TYPE = Utils.EXTRA_ASSIST;
+
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    public ExtraAssistDataTest() {
+        super();
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new ExtraAssistDataReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    public void testAssistContentAndAssistData() throws Exception {
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistDataNullness(false, false, false, false);
+
+        Log.i(TAG, "assist bundle is: " + Utils.toBundleString(mAssistBundle));
+
+        // tests that the assist content's structured data is the expected
+        assertEquals("AssistContent structured data did not match data in onProvideAssistContent",
+                Utils.getStructuredJSON(), mAssistContent.getStructuredData());
+        // tests the assist data. EXTRA_ASSIST_CONTEXT is what's expected.
+        Bundle extraExpectedBundle = Utils.getExtraAssistBundle();
+        Bundle extraAssistBundle = mAssistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
+        for (String key : extraExpectedBundle.keySet()) {
+            assertTrue("Assist bundle does not contain expected extra context key: " + key,
+                    extraAssistBundle.containsKey(key));
+            assertEquals("Extra assist context bundle values do not match for key: " + key,
+                    extraExpectedBundle.get(key), extraAssistBundle.get(key));
+        }
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+    }
+
+    private class ExtraAssistDataReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED)) {
+                if (mHasResumedLatch != null) {
+                    mHasResumedLatch.countDown();
+                }
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
index 40bf7a7..35f95a4 100644
--- a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
@@ -31,14 +31,13 @@
  * invoked on an app with FLAG_SECURE set.
  */
 public class FlagSecureTest extends AssistTestBase {
-
     static final String TAG = "FlagSecureTest";
 
     private static final String TEST_CASE_TYPE = Utils.FLAG_SECURE;
 
     private BroadcastReceiver mReceiver;
     private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
     public FlagSecureTest() {
         super();
     }
@@ -78,8 +77,13 @@
     }
 
     public void testSecureActivity() throws Exception {
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        waitForOnResume();
+        startSession();
+        waitForContext();
         verifyAssistDataNullness(false, false, false, false);
-
         // verify that we have only the root window and not its children.
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), true);
     }
@@ -89,9 +93,13 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(Utils.FLAG_SECURE_HASRESUMED)) {
-                mHasResumedLatch.countDown();
+                if (mHasResumedLatch != null) {
+                    mHasResumedLatch.countDown();
+                }
             } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
-                mAssistantReadyLatch.countDown();
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
             }
         }
     }
diff --git a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
new file mode 100644
index 0000000..f6b90b9
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.cts;
+
+import android.assist.cts.TestStartActivity;
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Test that triggering the Assistant causes the underlying Activity to lose focus **/
+
+public class FocusChangeTest extends AssistTestBase {
+    private static final String TAG = "FocusChangeTest";
+    private static final String TEST_CASE_TYPE = Utils.FOCUS_CHANGE;
+
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasGainedFocusLatch = new CountDownLatch(1);
+    private CountDownLatch mHasLostFocusLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new FocusChangeTestReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.GAINED_FOCUS);
+        filter.addAction(Utils.LOST_FOCUS);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitToGainFocus() throws Exception {
+        Log.i(TAG, "Waiting for the underlying activity to gain focus before continuing.");
+        if (!mHasGainedFocusLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to gain focus in " + Utils.TIMEOUT_MS + "msec.");
+        }
+    }
+
+    private void waitToLoseFocus() throws Exception {
+        Log.i(TAG, "Waiting for the underlying activity to lose focus.");
+        if (!mHasLostFocusLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity maintained focus despite the Assistant Firing"
+                 + Utils.TIMEOUT_MS + "msec.");
+        }
+    }
+
+    public void testLayerCausesUnderlyingActivityToLoseFocus() throws Exception {
+        mTestActivity.startTest(Utils.FOCUS_CHANGE);
+        waitForAssistantToBeReady(mReadyLatch);
+        mTestActivity.start3pApp(Utils.FOCUS_CHANGE);
+        waitToGainFocus();
+        startSession();
+        waitToLoseFocus();
+    }
+
+    private class FocusChangeTestReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.GAINED_FOCUS) && mHasGainedFocusLatch != null) {
+                mHasGainedFocusLatch.countDown();
+            } else if (action.equals(Utils.LOST_FOCUS) && mHasLostFocusLatch != null) {
+                mHasLostFocusLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
new file mode 100644
index 0000000..bc2ab80
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *  Test that the AssistStructure returned is properly formatted.
+ */
+
+public class LargeViewHierarchyTest extends AssistTestBase {
+    private static final String TAG = "LargeViewHierarchyTest";
+    private static final String TEST_CASE_TYPE = Utils.LARGE_VIEW_HIERARCHY;
+
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    public LargeViewHierarchyTest() {
+        super();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new LargeViewTestBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+    }
+
+    public void testTextView() throws Exception {
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistDataNullness(false, false, false, false);
+
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+                false /*FLAG_SECURE set*/);
+    }
+
+    private class LargeViewTestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+                mHasResumedLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) && mReadyLatch != null) {
+                mReadyLatch.countDown();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index 19a1be5..c886b74 100644
--- a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -16,7 +16,7 @@
 
 package android.assist.cts;
 
-import android.assist.TestStartActivity;
+import android.assist.cts.TestStartActivity;
 import android.assist.common.Utils;
 
 import android.app.Activity;
@@ -46,15 +46,18 @@
     private static final String action_onStop = Utils.LIFECYCLE_ONSTOP;
     private static final String action_onDestroy = Utils.LIFECYCLE_ONDESTROY;
 
+    private static final String TEST_CASE_TYPE = Utils.LIFECYCLE;
+
     private BroadcastReceiver mLifecycleTestBroadcastReceiver;
     private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
     private CountDownLatch mActivityLifecycleLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
         setUpAndRegisterReceiver();
-        startTestActivity(Utils.LIFECYCLE);
+        startTestActivity(TEST_CASE_TYPE);
     }
 
     @Override
@@ -96,7 +99,7 @@
 
     public void testLayerDoesNotTriggerLifecycleMethods() throws Exception {
         mTestActivity.startTest(Utils.LIFECYCLE);
-        waitForAssistantToBeReady();
+        waitForAssistantToBeReady(mReadyLatch);
         mTestActivity.start3pApp(Utils.LIFECYCLE);
         waitForOnResume();
         startSession();
@@ -108,16 +111,18 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(action_hasResumed)) {
+            if (action.equals(action_hasResumed) && mHasResumedLatch != null) {
                 mHasResumedLatch.countDown();
-            } else if (action.equals(action_onPause)) {
+            } else if (action.equals(action_onPause) && mActivityLifecycleLatch != null) {
                 mActivityLifecycleLatch.countDown();
-            } else if (action.equals(action_onStop)) {
+            } else if (action.equals(action_onStop) && mActivityLifecycleLatch != null) {
                 mActivityLifecycleLatch.countDown();
-            } else if (action.equals(action_onDestroy)) {
+            } else if (action.equals(action_onDestroy) && mActivityLifecycleLatch != null) {
                 mActivityLifecycleLatch.countDown();
             } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
-                mAssistantReadyLatch.countDown();
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
             }
         }
     }
diff --git a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
new file mode 100644
index 0000000..45082ae
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.DatabaseUtils;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+
+import junit.framework.Test;
+
+import java.lang.Exception;
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ScreenshotTest extends AssistTestBase {
+    static final String TAG = "ScreenshotTest";
+
+    private static final String TEST_CASE_TYPE = Utils.SCREENSHOT;
+
+    private BroadcastReceiver mScreenshotActivityReceiver;
+    private CountDownLatch mHasResumedLatch, mReadyLatch;
+
+    public ScreenshotTest() {
+        super();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mReadyLatch = new CountDownLatch(1);
+        // set up receiver
+        mScreenshotActivityReceiver = new ScreenshotTestReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        mContext.registerReceiver(mScreenshotActivityReceiver, filter);
+
+        // start test start activity
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mScreenshotActivityReceiver != null) {
+            mContext.unregisterReceiver(mScreenshotActivityReceiver);
+        }
+        super.tearDown();
+    }
+
+    public void testRedScreenshot() throws Exception {
+        Log.i(TAG, "Starting screenshot test");
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        Log.i(TAG, "start waitForAssistantToBeReady()");
+        waitForAssistantToBeReady(mReadyLatch);
+
+        waitForActivityResumeAndAssist(Color.RED);
+        verifyAssistDataNullness(false, false, false, false);
+        assertTrue(mScreenshotMatches);
+    }
+
+    public void testGreenScreenshot() throws Exception {
+        Log.i(TAG, "Starting screenshot test");
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        Log.i(TAG, "start waitForAssistantToBeReady()");
+        waitForAssistantToBeReady(mReadyLatch);
+
+        waitForActivityResumeAndAssist(Color.GREEN);
+        verifyAssistDataNullness(false, false, false, false);
+        assertTrue(mScreenshotMatches);
+    }
+
+    public void testBlueScreenshot() throws Exception {
+        Log.i(TAG, "Starting screenshot test");
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        Log.i(TAG, "start waitForAssistantToBeReady()");
+        waitForAssistantToBeReady(mReadyLatch);
+
+        waitForActivityResumeAndAssist(Color.BLUE);
+        verifyAssistDataNullness(false, false, false, false);
+        assertTrue(mScreenshotMatches);
+    }
+
+    private void waitForActivityResumeAndAssist(int color) throws Exception {
+        mHasResumedLatch = new CountDownLatch(1);
+        Bundle extras = new Bundle();
+        extras.putInt(Utils.SCREENSHOT_COLOR_KEY, color);
+        startSession(TEST_CASE_TYPE, extras);
+        Log.i(TAG, "waiting for onResume() before continuing.");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+        waitForContext();
+    }
+
+    private class ScreenshotTestReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.i(ScreenshotTest.TAG, "Got some broadcast: " + action);
+            if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+                Log.i(ScreenshotTest.TAG, "Received assist receiver is registered.");
+                if (mReadyLatch != null) {
+                    mReadyLatch.countDown();
+                }
+            } else if (action.equals(Utils.APP_3P_HASRESUMED)) {
+                if (mHasResumedLatch != null) {
+                    mHasResumedLatch.countDown();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/TestStartActivity.java b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
new file mode 100644
index 0000000..e54e774
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import java.lang.Override;
+
+public class TestStartActivity extends Activity {
+    static final String TAG = "TestStartActivity";
+
+    private ScrollView mScrollView;
+    private TextView mTextView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, " in onCreate");
+        // Set the respective view we want compared with the test activity
+        String testName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+        switch (testName) {
+            case Utils.ASSIST_STRUCTURE:
+                setContentView(R.layout.test_app);
+                setTitle(R.string.testAppTitle);
+                return;
+            case Utils.TEXTVIEW:
+                setContentView(R.layout.text_view);
+                mTextView =  (TextView) findViewById(R.id.text_view);
+                mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+                setTitle(R.string.textViewActivityTitle);
+                return;
+            case Utils.LARGE_VIEW_HIERARCHY:
+                setContentView(R.layout.multiple_text_views);
+                setTitle(R.string.testAppTitle);
+                return;
+            case Utils.WEBVIEW:
+                if (getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_WEBVIEW)) {
+                    setContentView(R.layout.webview);
+                    setTitle(R.string.webViewActivityTitle);
+                    WebView webview = (WebView) findViewById(R.id.webview);
+                    webview.setWebViewClient(new WebViewClient() {
+                        @Override
+                        public void onPageFinished(WebView view, String url) {
+                            sendBroadcast(new Intent(Utils.TEST_ACTIVITY_LOADED));
+                        }
+                    });
+                    webview.loadData(Utils.WEBVIEW_HTML, "text/html", "UTF-8");
+                }
+                return;
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.i(TAG, " in onResume");
+    }
+
+    public void startTest(String testCaseName) {
+        Log.i(TAG, "Starting test activity for TestCaseType = " + testCaseName);
+        Intent intent = new Intent();
+        intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
+        intent.setAction("android.intent.action.START_TEST_" + testCaseName);
+        intent.setComponent(new ComponentName("android.assist.service",
+                "android.assist." + Utils.getTestActivity(testCaseName)));
+        startActivity(intent);
+    }
+
+    public void start3pApp(String testCaseName) {
+        Intent intent = new Intent();
+        intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
+        intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
+        intent.setComponent(Utils.getTestAppComponent(testCaseName));
+        startActivity(intent);
+    }
+
+    public void start3pAppWithColor(String testCaseName, int color) {
+        Intent intent = new Intent();
+        intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
+        intent.putExtra(Utils.SCREENSHOT_COLOR_KEY, color);
+        intent.setComponent(Utils.getTestAppComponent(testCaseName));
+        startActivity(intent);
+    }
+
+    @Override
+    protected void onPause() {
+        Log.i(TAG, " in onPause");
+        super.onPause();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Log.i(TAG, " in onStart");
+    }
+
+    @Override protected void onRestart() {
+        super.onRestart();
+        Log.i(TAG, " in onRestart");
+    }
+
+    @Override
+    protected void onStop() {
+        Log.i(TAG, " in onStop");
+        super.onStop();
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.i(TAG, " in onDestroy");
+        super.onDestroy();
+    }
+
+    public void scrollText(int scrollX, int scrollY, boolean scrollTextView,
+            boolean scrollScrollView) {
+        if (scrollTextView) {
+            if (scrollX < 0 || scrollY < 0) {
+                scrollX = mTextView.getWidth();
+                scrollY = mTextView.getLayout().getLineTop(mTextView.getLineCount()) - mTextView.getHeight();
+            }
+            Log.i(TAG, "Scrolling text view to " + scrollX + ", " + scrollY);
+            mTextView.scrollTo(scrollX, scrollY);
+        } else if (scrollScrollView) {
+            if (scrollX < 0 || scrollY < 0) {
+                Log.i(TAG, "Scrolling scroll view to bottom right");
+                mScrollView.fullScroll(View.FOCUS_DOWN);
+                mScrollView.fullScroll(View.FOCUS_RIGHT);
+            } else {
+                Log.i(TAG, "Scrolling scroll view to " + scrollX + ", " + scrollY);
+                mScrollView.scrollTo(scrollX, scrollY);
+            }
+        }
+    }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/TextViewTest.java b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
new file mode 100644
index 0000000..e4390bc
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *  Test that the AssistStructure returned is properly formatted.
+ */
+
+public class TextViewTest extends AssistTestBase {
+    private static final String TAG = "TextViewTest";
+    private static final String TEST_CASE_TYPE = Utils.TEXTVIEW;
+
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    public TextViewTest() {
+        super();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new TextViewTestBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+    }
+
+    public void testTextView() throws Exception {
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        scrollTestApp(0, 0, true, false);
+
+        // Verify that the text view contains the right text
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistDataNullness(false, false, false, false);
+
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+                false /*FLAG_SECURE set*/);
+
+        // Verify that the scroll position of the text view is accurate after scrolling.
+        scrollTestApp(10, 50, true /* scrollTextView */, false /* scrollScrollView */);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+        scrollTestApp(-1, -1, true, false);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+        scrollTestApp(0, 0, true, true);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+        scrollTestApp(10, 50, false, true);
+        waitForOnResume();
+        startSession();
+        waitForContext();
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+    }
+
+    private class TextViewTestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+                mHasResumedLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) &&  mReadyLatch != null) {
+                mReadyLatch.countDown();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/WebViewTest.java b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
new file mode 100644
index 0000000..e367e22
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *  Test that the AssistStructure returned is properly formatted.
+ */
+
+public class WebViewTest extends AssistTestBase {
+    private static final String TAG = "WebViewTest";
+    private static final String TEST_CASE_TYPE = Utils.WEBVIEW;
+
+    private boolean mWebViewSupported;
+    private BroadcastReceiver mReceiver;
+    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mTestWebViewLatch = new CountDownLatch(1);
+    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+    public WebViewTest() {
+        super();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setUpAndRegisterReceiver();
+        startTestActivity(TEST_CASE_TYPE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void setUpAndRegisterReceiver() {
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mReceiver = new WebViewTestBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+        filter.addAction(Utils.TEST_ACTIVITY_LOADED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    private void waitForOnResume() throws Exception {
+        Log.i(TAG, "waiting for onResume() before continuing");
+        if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+    }
+
+    private void waitForTestActivity() throws Exception {
+        Log.i(TAG, "waiting for webview in test activity to load");
+        if (!mTestWebViewLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            // wait for webView to load completely.
+        }
+    }
+
+    public void testWebView() throws Exception {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+            return;
+        }
+        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        mTestActivity.startTest(TEST_CASE_TYPE);
+        waitForAssistantToBeReady(mReadyLatch);
+        waitForOnResume();
+        waitForTestActivity();
+        startSession();
+        waitForContext();
+        verifyAssistDataNullness(false, false, false, false);
+        verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+                false /*FLAG_SECURE set*/);
+    }
+
+    private class WebViewTestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+                mHasResumedLatch.countDown();
+            } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) && mReadyLatch != null) {
+                mReadyLatch.countDown();
+            } else if (action.equals(Utils.TEST_ACTIVITY_LOADED) && mTestWebViewLatch != null) {
+                mTestWebViewLatch.countDown();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/AndroidManifest.xml b/tests/tests/assist/testapp/AndroidManifest.xml
index 8d6169c..fa08f55 100644
--- a/tests/tests/assist/testapp/AndroidManifest.xml
+++ b/tests/tests/assist/testapp/AndroidManifest.xml
@@ -18,33 +18,79 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.assist.testapp">
 
+    <uses-permission android:name="android.permission.INTERNET" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
-        <activity android:name="TestApp"
-                android:label="Assist Test App"
-                android:theme="@android:style/Theme.Material.Light">
+        <activity android:name=".TestApp"
+                android:label="Assist Structure Test Activity">
           <intent-filter>
               <action android:name="android.intent.action.TEST_APP_ASSIST_STRUCTURE" />
+              <action android:name="android.intent.action.TEST_APP_LARGE_VIEWHIERARCHY" />
               <category android:name="android.intent.category.DEFAULT" />
               <category android:name="android.intent.category.VOICE" />
           </intent-filter>
         </activity>
-        <activity android:name="SecureActivity"
-                  android:label="Secure Test App"
-                  android:theme="@android:style/Theme.Material.Light">
+        <activity android:name=".DisableContextActivity"
+            android:label="Disable Context Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_DISABLE_CONTEXT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".SecureActivity"
+                  android:label="Secure Test Activity">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_APP_FLAG_SECURE" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE" />
             </intent-filter>
         </activity>
-        <activity android:name="LifecycleActivity"
-                  android:label="Life Cycle Check Activity"
-                  android:theme="@android:style/Theme.Material.Light">
+        <activity android:name=".LifecycleActivity"
+                  android:label="Life Cycle Check Activity">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_APP_LIFECYCLE" />
                 <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".ScreenshotActivity"
+                  android:label="Screenshot Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_SCREENSHOT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".ExtraAssistDataActivity"
+            android:label="Extra Assist Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_EXTRA_ASSIST" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".TextViewActivity"
+            android:label="TextView Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_TEXTVIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".WebViewActivity"
+            android:label="WebView Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_WEBVIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".FocusChangeActivity"
+            android:label="Focus Change Test Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_APP_FOCUS_CHANGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE" />
             </intent-filter>
         </activity>
     </application>
diff --git a/tests/tests/assist/testapp/res/layout/multiple_text_views.xml b/tests/tests/assist/testapp/res/layout/multiple_text_views.xml
new file mode 100644
index 0000000..455d5e3
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/multiple_text_views.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:orientation="vertical">
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:scrollbars="vertical"
+    android:text="@string/text_too_large_to_fit" />
+
+  <ScrollView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_weight="1">
+    <TextView
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:scrollbars="vertical"
+      android:text="@string/text_too_large_to_fit" />
+  </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/screenshot_activity.xml b/tests/tests/assist/testapp/res/layout/screenshot_activity.xml
new file mode 100644
index 0000000..05051dc
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/screenshot_activity.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/screenshot_activity"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/secure_app.xml b/tests/tests/assist/testapp/res/layout/secure_app.xml
index 9169a37..3b72ad6 100644
--- a/tests/tests/assist/testapp/res/layout/secure_app.xml
+++ b/tests/tests/assist/testapp/res/layout/secure_app.xml
@@ -14,12 +14,12 @@
      limitations under the License.
 -->
 <RelativeLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
     <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/welcome" />
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/welcome" />
 </RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/test_app.xml b/tests/tests/assist/testapp/res/layout/test_app.xml
index 9169a37..3fbfd6d 100644
--- a/tests/tests/assist/testapp/res/layout/test_app.xml
+++ b/tests/tests/assist/testapp/res/layout/test_app.xml
@@ -13,13 +13,35 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
     <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/welcome" />
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/welcome" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="350dp"
+        android:orientation="vertical"
+        android:layout_gravity="bottom">
+        <FrameLayout
+            android:id="@+id/card1"
+            android:layout_width="match_parent"
+            android:layout_height="150dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginTop="16dp"
+            android:elevation="3dp">
+        </FrameLayout>
+        <View
+            android:id="@+id/card2"
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginTop="16dp"
+            android:elevation="3dp"/>
+    </LinearLayout>
 </RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/text_view.xml b/tests/tests/assist/testapp/res/layout/text_view.xml
new file mode 100644
index 0000000..0f0f427
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/text_view.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <TextView
+        android:id="@+id/text_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:focusable="false"
+        android:focusableInTouchMode="false"
+        android:scrollbars="vertical"
+        android:clickable="true"
+        android:text="@string/text_too_large_to_fit" />
+
+    <ScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:scrollbars="vertical"
+            android:text="@string/text_too_large_to_fit" />
+    </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/webview.xml b/tests/tests/assist/testapp/res/layout/webview.xml
new file mode 100644
index 0000000..bdb8082
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/webview.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    </WebView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/values/strings.xml b/tests/tests/assist/testapp/res/values/strings.xml
index a245b36..670024b 100644
--- a/tests/tests/assist/testapp/res/values/strings.xml
+++ b/tests/tests/assist/testapp/res/values/strings.xml
@@ -1,4 +1,284 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
 <resources>
+
+    <string name="app_name">Memegen</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
     <string name="welcome">Hello there!</string>
-</resources>
\ No newline at end of file
+    <string name="text_too_large_to_fit">❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € →Hello هتاف للترحيب שלום
+        përshëndetje Добры дзень 您好 হ্যালো здравей მიესალმები Χαίρετε હેલો नमस्ते Nnọọ こんにちは ಹಲೋ
+        Сәлеметсіз бе ជំរាបសួរ 안녕하세요 ສະບາຍດີ ഹലോ हॅलो Сайн байна уу नमस्ते سلامהעלא ہیلو
+        မင်္ဂလာပါ ਸਤ ਸ੍ਰੀ ਅਕਾਲ Здравствуйте здраво ආයුබෝවන් ஹலோ హలో สวัสดี Pẹlẹ o
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+        convallis nunc et vestibulum. Sed et consequat quam, blandit varius tortor. Curabitur
+        accumsan nulla lectus, placerat condimentum odio elementum vel. Nulla erat ex, accumsan ut
+        enim sagittis, scelerisque efficitur ante. Nullam quis orci nec magna maximus malesuada ac
+        id sem. Nam sagittis erat risus, a accumsan neque congue sit amet. Nullam risus velit,
+        faucibus eget scelerisque et, maximus eget arcu. Sed porta sed libero ac imperdiet.
+
+        Nulla sem lectus, ullamcorper id dui vel, rutrum interdum augue. Proin aliquam nisi vitae
+        hendrerit tempor. Mauris porttitor velit et egestas feugiat. Vivamus eu dapibus libero,
+        quis fringilla urna. Suspendisse non turpis dui. Vivamus facilisis diam vitae est auctor
+        luctus. Etiam quis lectus viverra, interdum turpis eu, aliquam sem. Nulla vulputate lacinia
+        nisi a dictum. Cras faucibus vitae tortor at ullamcorper. Quisque sit amet sapien maximus,
+        ornare nisi non, imperdiet magna. Vestibulum tempor metus ac mi ultrices dapibus.
+
+        Suspendisse potenti. Mauris pellentesque lacinia tristique. Pellentesque vel dui quis sem
+        lacinia imperdiet feugiat vitae sem. Proin a arcu magna. Sed quis augue eu mi accumsan
+        pellentesque pretium in leo. Duis euismod purus mauris, ac tempor erat auctor non. Quisque
+        bibendum est pulvinar ex dapibus, ac tincidunt nibh tempus. Mauris sodales sem id purus
+        commodo iaculis. Pellentesque a quam dapibus, vehicula lectus at, tincidunt arcu. In
+        placerat porttitor urna quis consequat. Nullam feugiat nisl sed urna hendrerit, sed
+        elementum massa iaculis. Fusce sit amet turpis hendrerit, varius lorem sed, luctus mi.
+        Phasellus sit amet ex orci. Duis scelerisque nisl quis efficitur maximus. Curabitur vitae
+        accumsan nunc, eget varius nisi.
+
+        Fusce efficitur malesuada luctus. Aliquam dapibus tortor sit amet purus semper, sit amet
+        pretium lorem feugiat. Maecenas gravida sed arcu et placerat. Nulla facilisi. Cras placerat
+        rutrum mi, in rutrum mauris maximus at. Mauris eu suscipit ante. Nullam pharetra egestas
+        diam a viverra. Donec sem turpis, tempor malesuada est vel, blandit accumsan magna. In
+        iaculis velit in efficitur hendrerit. Nulla facilisi. Curabitur eget ligula lorem. Sed sit
+        amet dolor ut ligula malesuada condimentum. Phasellus molestie augue eget libero commodo,
+        vel blandit ex blandit.
+
+        Morbi cursus tortor ante, et tempus nisi tempus et. Suspendisse quis gravida diam. Aliquam
+        efficitur dolor sit amet sollicitudin varius. Etiam libero purus, ornare nec nulla vel,
+        ullamcorper blandit nisl. Sed vel consequat diam, id placerat sem. Donec quis elementum
+        urna. In posuere bibendum nunc, in condimentum justo blandit ac. Quisque enim lorem,
+        gravida at purus at, sollicitudin imperdiet erat. Ut consectetur rutrum ante, et pretium
+        odio iaculis a. Nullam a nibh vulputate, volutpat lectus eu, pellentesque felis. Nam
+        vehicula suscipit diam nec convallis. Quisque congue maximus sem, sit amet hendrerit leo
+        tempor et.
+
+        Nam eu consequat dui. Sed semper dignissim mattis. Integer tortor eros, tempor in lectus a,
+        lobortis aliquam dolor. Phasellus at sagittis magna. Nulla eleifend orci ac urna auctor,
+        sit amet luctus urna vulputate. Nulla venenatis venenatis erat ac finibus. Etiam
+        ullamcorper elementum suscipit. Morbi nec velit non mauris porta finibus. Nullam in
+        sagittis odio. Praesent eget nisl ut mauris vestibulum feugiat. Sed vulputate at elit et
+        cursus. Praesent viverra erat blandit nunc egestas, vel feugiat ex condimentum. Class
+        aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+
+        Nulla fermentum mattis urna, non gravida eros vestibulum et. Fusce porttitor augue turpis,
+        sit amet aliquam augue sodales non. Nunc neque odio, sagittis sed gravida euismod, commodo
+        at libero. Donec porttitor pulvinar neque vitae lobortis. Aliquam accumsan velit nec sapien
+        placerat egestas. Aliquam at tincidunt massa, et dignissim justo. Donec sapien ante, rutrum
+        et tristique a, commodo a massa.
+
+        Nunc placerat lobortis magna, et molestie lacus semper porta. Lorem ipsum dolor sit amet,
+        consectetur adipiscing elit. Phasellus ac ligula dui. Duis ultrices viverra eros fermentum
+        finibus. Integer ultrices, felis in accumsan volutpat, mi ligula hendrerit nunc, nec
+        accumsan mauris tellus sit amet metus. Ut pharetra enim et sapien sollicitudin, nec
+        ultricies urna pharetra. Morbi non tortor nec dui feugiat rutrum. Aliquam malesuada sodales
+        risus, sed congue nunc accumsan vitae. Etiam nunc magna, tempus non suscipit eu, feugiat ut
+        nibh. Maecenas et libero ut nisl pellentesque tempor nec vel quam. Etiam sem ligula,
+        ullamcorper non dolor a, viverra placerat nulla. Nullam dictum commodo dui, sed ultrices
+        enim sagittis eget. Morbi non consectetur lectus. In gravida, augue vitae pulvinar
+        molestie, ligula orci vulputate ex, at bibendum urna felis nec nibh. Sed nisl nunc, iaculis
+        at augue venenatis, fringilla accumsan velit. Curabitur nec augue porttitor, rutrum nisi
+        vitae, elementum orci.
+
+        Vestibulum eu tortor iaculis, dignissim velit quis, rhoncus dolor. Donec et tincidunt
+        nulla. Duis faucibus auctor erat ac ultricies. In a fermentum mi. Fusce vitae mi id sem
+        interdum tincidunt. Nulla hendrerit orci turpis, in maximus elit mollis eget. Aliquam erat
+        volutpat. Phasellus mattis est nibh, ut scelerisque ligula egestas eu. Ut molestie orci a
+        malesuada tempor. Sed tempus arcu id orci gravida faucibus. Vivamus ac lacinia neque, at
+        vehicula magna.
+
+        Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Nullam aliquam, justo scelerisque egestas sodales, purus odio posuere arcu, ac ultrices
+        nunc felis non massa. Aliquam vulputate ipsum sed aliquet auctor. In pulvinar, eros sit
+        amet ultricies tristique, lacus ipsum scelerisque eros, nec vestibulum est lectus quis
+        lorem. Pellentesque ac augue ut eros mattis viverra vitae ut lacus. Phasellus imperdiet
+        efficitur elit eget tincidunt. Donec sodales metus at dolor pulvinar, at gravida nibh
+        facilisis. Sed nec tellus luctus, cursus lacus sed, euismod orci. Maecenas sit amet leo
+        orci. Nulla non leo non mi eleifend consequat sit amet vitae dui. Sed gravida gravida
+        justo, tincidunt ultrices justo semper vitae. Fusce at nisi nisl. Morbi molestie quis justo
+        a convallis. Curabitur massa lacus, feugiat quis mauris ac, malesuada viverra est.
+
+        Phasellus bibendum faucibus velit, ac scelerisque velit tincidunt eu. Curabitur quis
+        suscipit erat, ac feugiat odio. Nullam et sapien et nibh maximus posuere. Vivamus faucibus
+        justo eget dictum sollicitudin. Etiam at leo eget elit facilisis lobortis. Maecenas
+        bibendum tortor non erat pretium dignissim. Fusce id imperdiet augue. Suspendisse dignissim
+        magna vel odio viverra varius. Maecenas suscipit ante et lorem sodales vehicula. Quisque
+        vel magna id sem suscipit iaculis. Etiam in elementum risus.
+
+        Suspendisse odio nisi, pharetra et purus sit amet, placerat lobortis diam. Phasellus enim
+        nunc, posuere sed porta in, ornare eu ipsum. Phasellus imperdiet porta neque, vitae dapibus
+        tellus feugiat eget. Nulla sodales leo ac efficitur luctus. Vivamus eget ipsum quis ante
+        pulvinar blandit. Vestibulum a justo convallis justo elementum viverra ut sit amet nisl.
+        Suspendisse eget augue fermentum, sagittis risus a, rhoncus elit. Vestibulum in maximus
+        tortor, non vestibulum libero. Nunc accumsan neque a nisl dapibus, id laoreet neque congue.
+        Pellentesque sapien odio, fringilla non nulla nec, varius placerat diam. Aliquam
+        consectetur neque eu ipsum posuere, nec dignissim sem faucibus. Donec sit amet tempor
+        sapien. Nam at libero vel lorem dapibus ultrices a vel augue. Nunc facilisis justo ante,
+        mollis tristique velit aliquet quis. Mauris consectetur odio at urna bibendum aliquam.
+
+           Nullam lectus orci, hendrerit ut ultrices in, dapibus pellentesque nibh. Aliquam arcu
+        metus, lobortis vel dignissim id, tempus ut ante. Integer vitae ante augue. In hac habitasse
+        platea dictumst. Vestibulum in tellus ante. Cras nisi tellus, congue ac velit quis, rhoncus
+        ornare ligula. Sed facilisis gravida pellentesque. Praesent id ultrices orci, ac ultricies
+        arcu. Donec at ante quis augue faucibus congue. Donec mattis quam dui, ut vestibulum orci
+        tempor mattis. Phasellus in quam id tortor varius ullamcorper ac ac ante. Proin cursus
+        accumsan sem, vel finibus lectus eleifend ut. Donec efficitur feugiat diam id ultricies. In
+        quis euismod nisi. Vestibulum eget viverra sapien. Donec scelerisque nec elit vel viverra.
+
+           Sed mi urna, rutrum quis augue vel, aliquet placerat diam. Proin faucibus in odio et
+        consequat. Proin ut ex in mauris eleifend efficitur. Praesent ullamcorper sollicitudin urna,
+        sed mollis elit hendrerit non. Duis leo lorem, efficitur eu auctor sit amet, scelerisque
+        scelerisque magna. Mauris eget massa auctor, viverra arcu a, elementum nibh. Sed
+        pellentesque, nulla sed condimentum posuere, tortor metus congue sem, nec placerat neque
+        magna vitae purus.
+
+           Etiam at risus vitae sapien aliquam condimentum. Vestibulum id libero placerat purus
+        vehicula consectetur. Pellentesque sapien sapien, posuere at pulvinar at, ultrices sed est.
+        Maecenas nec condimentum ante. Aenean volutpat, ex condimentum hendrerit hendrerit, quam
+        nisl  pharetra nibh, vitae bibendum nisl odio vel lacus. Morbi mi tellus, bibendum id mauris
+        eu,  facilisis volutpat turpis. Maecenas rutrum convallis felis. Quisque eget feugiat felis.
+        Duis pellentesque iaculis massa ut facilisis. Donec nec commodo magna. Integer aliquet orci
+        a odio eleifend elementum. Quisque molestie, urna ut molestie eleifend, odio neque maximus
+        enim, eget viverra metus lectus eget quam. Fusce nec urna ac neque bibendum aliquam vel quis
+        dui. Fusce ac quam consequat, feugiat leo vitae, auctor felis.
+
+           Sed ac metus mauris. Sed sed velit ut tortor aliquam vestibulum at eu arcu. Etiam eu
+        posuere lacus. Maecenas id lacus quis sem mollis sodales. Quisque justo sapien, vulputate ac
+        mi ut, congue vestibulum orci. Donec euismod erat rutrum, laoreet urna sed, accumsan purus.
+        Donec eu quam a sapien condimentum accumsan. Sed at tellus lorem. Curabitur bibendum, arcu
+        sit amet finibus sodales, mi sem finibus sem, eget scelerisque tellus neque vel urna.
+        Suspendisse eu augue nec erat suscipit luctus sed non metus.
+
+           Suspendisse porttitor ex ipsum. Pellentesque tristique eros sed pharetra porttitor.
+        Quisque ut elit vehicula, aliquet est nec, faucibus tellus. Donec ex augue, congue eu
+        dignissim  maximus, vestibulum at purus. Nulla quam enim, laoreet sit amet molestie vel,
+        dapibus nec tortor. Sed interdum massa ac orci gravida, vel viverra lacus lacinia. Donec
+        nisl lacus, fermentum at faucibus ac, consequat ut nibh. Praesent laoreet est augue, vitae
+        maximus dui efficitur sit amet. Cras ipsum tellus, tincidunt at volutpat non, tincidunt ut
+        elit. Morbi commodo sagittis gravida. Pellentesque sed ante massa. Phasellus a turpis non
+        turpis cursus consequat sed nec tortor. Proin et augue elit.
+
+           Duis finibus sem commodo rutrum pulvinar. In sollicitudin ante magna, vel facilisis
+        tellus fringilla vel. Nam purus ex, tincidunt eget varius at, euismod nec elit. Curabitur
+        consequat nulla vel nisi iaculis, ut mattis odio congue. Nulla et mollis tortor, a maximus
+        justo. Donec semper eros sed nunc rhoncus condimentum. Donec nibh purus, interdum non lectus
+        id, porta convallis eros.
+
+           Sed hendrerit, dui non sagittis sollicitudin, enim ex imperdiet sapien, et maximus lorem
+        dolor a enim. Nulla risus nisl, vestibulum at ornare posuere, congue in felis. Duis sagittis
+        id diam a varius. Donec viverra eu orci sodales commodo. Cras metus tortor, sodales vitae
+        auctor non, scelerisque a ante. Quisque sodales nisi libero, ut lobortis enim suscipit ut.
+        Cras mi ipsum, maximus non bibendum sit amet, pharetra quis ipsum. Vestibulum venenatis, odi
+        at hendrerit pretium, tellus diam auctor justo, non posuere quam mauris id nisl. Nam dolor
+        nibh, molestie et lectus et, scelerisque porta elit. Vestibulum viverra condimentum auctor.
+        In eros tortor, convallis sed quam eu, ultrices malesuada purus. Integer lorem quam,
+        ultricies at est consectetur, sagittis porttitor eros. Proin non risus vitae lacus
+        consectetur malesuada non pulvinar justo. Aliquam mollis nisi nunc, sit amet vulputate metus
+        sollicitudin vel.
+
+           Quisque auctor varius fermentum. Praesent mollis justo sit amet est consectetur, in
+        volutpat tellus mollis. Aenean at bibendum eros, at finibus nibh. Phasellus nec mi sem.
+        Mauris pellentesque dui sit amet lobortis aliquam. Ut nec massa at urna aliquam gravida vel
+        in magna. Donec consectetur sapien magna, a auctor sapien placerat eu.
+
+           Pellentesque aliquet ante sed lacus gravida rutrum. Maecenas euismod varius felis, nec
+        tempus metus tempus et. Nam convallis augue a massa scelerisque, vel pharetra dolor
+        scelerisque. Fusce porttitor mi a magna rutrum condimentum. Aliquam fermentum at turpis at
+        auctor. Nulla ut suscipit dui. Donec rutrum viverra aliquam. Donec elementum nisl sapien, ac
+        blandit risus porta facilisis. Proin tellus dolor, ornare vel magna sit amet, maximus
+        volutpat felis. Aenean euismod aliquet purus, at finibus nunc elementum eu. Ut faucibus
+        ullamcorper consectetur. Aenean egestas arcu id mauris elementum, at sollicitudin est
+        congue. Sed a odio mattis, sollicitudin erat ut, tristique dolor. Aliquam luctus risus quis
+        tellus semper, a vestibulum nulla viverra.
+
+           Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
+        himenaeos. Vestibulum sit amet nisi felis. Praesent condimentum consequat lacus pulvinar
+        imperdiet. Praesent vel condimentum quam. Maecenas eu aliquet odio. Vestibulum sed nulla
+        mattis lacus porta bibendum a ac eros. Nunc porttitor sagittis laoreet. Duis porta eros at
+        congue tristique. Pellentesque quis fringilla neque, a hendrerit tellus. Pellentesque ac
+        nibh ac tellus pulvinar porttitor et in est. Integer blandit lorem libero, eu pulvinar
+        tellus posuere eget. Vivamus pretium nulla ligula, ut dapibus massa fringilla in.
+        Suspendisse consectetur sem non elit porta, id pellentesque erat dapibus. Quisque ex mi,
+        tempus et hendrerit nec, gravida quis odio. Ut at mi in leo scelerisque venenatis.
+
+           Ut sed tellus in risus tincidunt tempor ut at arcu. Maecenas ut convallis justo. In
+        rutrum urna eu massa rhoncus, eget condimentum augue vehicula. Nullam eget placerat erat.
+        Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+        Aenean at volutpat orci, a lobortis dolor. Sed consequat facilisis interdum. Fusce libero
+        neque, fringilla in congue a, vehicula rutrum ipsum. Nam ornare placerat vestibulum. Proin
+        nec orci velit. Pellentesque scelerisque gravida diam, ut tristique libero tempus eu. Nam
+        semper lacus nec nulla volutpat imperdiet non eget tortor. Sed et pellentesque ligula.
+
+           Aenean a dolor dolor. Curabitur ut placerat lacus, sit amet aliquet orci. Aliquam erat
+        volutpat. Cras mollis sit amet lectus ornare pretium. Vestibulum fringilla orci vel est
+        iaculis, at mattis quam condimentum. Vivamus semper elit consectetur lectus gravida, in
+        sollicitudin mi fringilla. Donec eget lorem in orci blandit aliquam. Pellentesque libero
+        tellus, dignissim id augue et, vulputate viverra nisl. Cum sociis natoque penatibus et
+        magnis dis parturient montes, nascetur ridiculus mus. Donec ac vulputate metus, eu suscipit
+        sem. Donec placerat, nulla at sodales hendrerit, orci tortor vestibulum purus, non pharetra
+        nulla purus posuere arcu.
+
+           Quisque feugiat elit sem, ac interdum diam pharetra nec. Curabitur sem libero, vulputate
+        eu libero vitae, volutpat facilisis ligula. Aenean maximus erat laoreet, interdum ante in,
+        ultrices nisi. Nullam nec efficitur sapien. Integer feugiat et tortor ac bibendum. Donec a
+        scelerisque tortor. Cras quis viverra diam, vitae viverra ipsum. Aliquam ultrices neque sem,
+        congue sodales elit tempus sit amet. Pellentesque habitant morbi tristique senectus et netus
+        et malesuada fames ac turpis egestas. Sed dignissim ipsum eget diam rhoncus, ut finibus
+        nulla pretium. Morbi suscipit nibh vel nisl posuere molestie. Maecenas posuere turpis et
+        rutrum consectetur. Morbi venenatis arcu non gravida vulputate. Vivamus auctor tellus
+        ullamcorper ligula vestibulum cursus. Nunc gravida sit amet nisl quis facilisis.
+
+           Praesent ut justo vestibulum, accumsan mi et, feugiat purus. Nullam pulvinar iaculis
+        pharetra. Aliquam pulvinar risus sit amet elit suscipit tincidunt. In hac habitasse platea
+        dictumst. Etiam eget velit ac magna lacinia efficitur. Vestibulum ante ipsum primis in
+        faucibus orci luctus et ultrices posuere cubilia Curae; Cras volutpat tempus sollicitudin.
+        Ut et ante elit.
+
+           Sed ac tortor justo. Fusce sed metus libero. Duis sagittis tortor ac ante sollicitudin,
+        nec efficitur nibh euismod. Donec porttitor cursus quam, in aliquam lorem feugiat ut.
+        Aliquam tempor lacus eu feugiat feugiat. Nunc pulvinar, libero at auctor commodo, metus diam
+        commodo lorem, in feugiat elit ex non ligula. Quisque at vestibulum sapien, nec facilisis
+        neque. Aenean luctus, arcu ut rhoncus luctus, est massa rhoncus mauris, nec luctus urna sem
+        quis massa. Nam elit felis, congue et ligula eget, ultricies tincidunt erat. Vivamus
+        eleifend no dui ac dictum. In nulla justo, pulvinar ut tristique sed, congue et orci.
+
+           Quisque imperdiet mi lectus, ac scelerisque augue posuere ut. Duis non pulvinar ipsum,
+        finibus risus. Donec ullamcorper nisl at sodales lobortis. Mauris neque leo, vestibulum sit
+        amet placerat vel, aliquet vel sapien. Morbi massa tellus, scelerisque quis nisl in, feugiat
+        posuere augue. Aenean congue sem ut ipsum vulputate rhoncus vitae at eros. Maecenas in velit
+        orci pellentesque lobortis ac at felis. Vestibulum odio quam, lacinia dapibus ornare eu,
+        vulputate a eros. Curabitur eleifend ornare tellus, non sollicitudin justo viverra in. Sed
+        sodales neque et lacus semper, in pharetra est consequat. Nunc vehicula volutpat lectus, sit
+        amet scelerisque nisi. Aenean sollicitudin, sem at ultricies efficitur, eros metus
+        nisl, et fringilla felis lacus non orci. Praesent eros libero, finibus in purus id,
+        tempor ipsum. Donec suscipit libero velit. Aliquam quis diam pharetra, cursus ipsum in,
+        gravida metus. Maecenas ultrices ligula a ullamcorper scelerisque.
+
+           Donec tincidunt felis turpis, id venenatis neque convallis in. Proin euismod ligula nec
+        urna vulputate, sed elementum mauris ultrices. Ut efficitur sem vel mi vestibulum placerat.
+        Sed fermentum lacus nec metus dictum, a commodo quam fermentum. Sed vel vulputate magna.
+        Integer convallis nisi sit amet mi lobortis pellentesque. In egestas porttitor elit eu
+        scelerisque. Suspendisse eleifend vel enim quis tincidunt. Sed placerat risus et pretium
+        porttitor. Nam justo mi, cursus eu pellentesque vel, bibendum ut nisl. Nulla condimentum
+        lorem, non sagittis lorem volutpat vitae. Mauris nec libero lorem. Vestibulum lacus ex,
+        vulputate non massa vitae, pellentesque vestibulum dolor.
+
+           Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+        Suspendisse vitae erat nisl. Vestibulum elit ante, semper et semper sit amet, fringilla
+        sapien. Morbi ac nisi sit amet turpis tincidunt mattis ac eget nisl. Nunc a venenatis quam,
+        facilisis maximus odio. Aliquam erat volutpat. Maecenas leo enim, ornare a magna quis,
+        venenatis ornare nulla. Aliquam venenatis nibh et elit tincidunt, ut egestas lorem finibus.
+        Integer iaculis dolor sed enim blandit vestibulum. Nullam vel libero ultricies, sagittis
+        tortor non, molestie eros.</string>
+</resources>
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ExtraAssistDataActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ExtraAssistDataActivity.java
new file mode 100644
index 0000000..57d34f8
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ExtraAssistDataActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.assist.testapp;
+
+import android.app.Activity;
+import android.app.assist.AssistContent;
+import android.assist.common.Utils;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.Override;
+
+/**
+ * Test the onProvideAssistData and onProvideAssistContent methods activities may override to
+ * provide extra information to the assistant. Verify that the data passed from the activity matches
+ * the data received in {@link android.service.voice.VoiceInteractionSession}.
+ */
+public class ExtraAssistDataActivity extends Activity {
+    private static final String TAG = "ExtraAssistDataActivity";
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void onProvideAssistData(Bundle data) {
+        super.onProvideAssistData(data);
+        Log.i(TAG, "onProvideAssistData");
+        Utils.addExtraAssistDataToBundle(data);
+    }
+
+    @Override
+    public void onProvideAssistContent(AssistContent outContent) {
+        super.onProvideAssistContent(outContent);
+        Log.i(TAG, "onProvideAssistContent");
+        try {
+            outContent.setStructuredData(Utils.getStructuredJSON());
+        } catch (Exception e) {
+            Log.i(TAG, "Failed to get Structured JSON to put into the AssistContent.");
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java
new file mode 100644
index 0000000..4ab24ed
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.testapp;
+
+import android.app.Activity;
+import android.assist.common.Utils;
+import android.content.Intent;
+import android.util.Log;
+
+public class FocusChangeActivity extends Activity {
+    private static final String TAG = "FocusChangeActivity";
+    private boolean mGainedFocus = false;
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        if (hasFocus && !mGainedFocus) {
+            mGainedFocus = true;
+            Log.i(TAG, "gained focus");
+            sendBroadcast(new Intent(Utils.GAINED_FOCUS));
+        } else if (!hasFocus && mGainedFocus) {
+            Log.i(TAG, "lost focus");
+            sendBroadcast(new Intent(Utils.LOST_FOCUS));
+        }
+    }
+}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
index 4e1dc80..af10f99 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
@@ -39,22 +39,22 @@
 
     @Override
     protected void onPause() {
-        super.onPause();
         Log.i(TAG, "activity was paused");
         sendBroadcast(new Intent("android.intent.action.lifecycle_onpause"));
+        super.onPause();
     }
 
     @Override
     protected void onStop() {
-        super.onStop();
         Log.i(TAG, "activity was stopped");
         sendBroadcast(new Intent("android.intent.action.lifecycle_onstop"));
+        super.onStop();
     }
 
     @Override
     protected void onDestroy() {
-        super.onDestroy();
         Log.i(TAG, "activity was destroyed");
         sendBroadcast(new Intent("android.intent.action.lifecycle_ondestroy"));
+        super.onDestroy();
     }
 }
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ScreenshotActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ScreenshotActivity.java
new file mode 100644
index 0000000..581af2e
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ScreenshotActivity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.assist.testapp;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import java.lang.Override;
+
+public class ScreenshotActivity extends Activity {
+    static final String TAG = "ScreenshotActivity";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "ScreenshotActivity created");
+        setContentView(R.layout.screenshot_activity);
+    }
+
+    @Override
+    public void onResume() {
+        Log.i(TAG, " in onResume");
+        super.onResume();
+        int backgroundColor = getIntent().getIntExtra(Utils.SCREENSHOT_COLOR_KEY, Color.WHITE);
+        View view = findViewById(R.id.screenshot_activity);
+        view.setBackgroundColor(backgroundColor);
+        view.requestLayout();
+
+        // Tell service activity is in foreground.
+        Intent intent = new Intent(Utils.APP_3P_HASRESUMED);
+        sendBroadcast(intent);
+        Log.i(TAG, "Resumed broadcast sent.");
+    }
+
+    @Override
+    public void onPause() {
+        Log.i(TAG, "onPause");
+        finish();
+        super.onPause();
+    }
+}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
index d9b2ff2..708061e 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
@@ -16,11 +16,16 @@
 
 package android.assist.testapp;
 
+import android.assist.common.Utils;
+
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
 
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.WindowManager;
 
 public class SecureActivity extends Activity {
@@ -37,7 +42,15 @@
     @Override
     protected void onResume() {
         super.onResume();
-        Log.i(TAG, "Activity has resumed");     
-        sendBroadcast(new Intent("android.intent.action.flag_secure_hasResumed"));
+        Log.i(TAG, "Activity has resumed");
+        final View layout = findViewById(android.R.id.content);
+        ViewTreeObserver vto = layout.getViewTreeObserver();
+        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+            @Override
+            public void onGlobalLayout() {
+                layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                sendBroadcast(new Intent(Utils.FLAG_SECURE_HASRESUMED));
+            }
+        });
     }
 }
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
index 85a9342..e0f83cc 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -16,25 +16,59 @@
 
 package android.assist.testapp;
 
+import android.assist.common.Utils;
+
 import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import java.io.ByteArrayOutputStream;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import java.lang.Override;
 
 public class TestApp extends Activity {
     static final String TAG = "TestApp";
 
-    Bundle mTestinfo = new Bundle();
-    Bundle mTotalInfo = new Bundle();
+    private String mTestCaseName;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         Log.i(TAG, "TestApp created");
-        getLayoutInflater().inflate(R.layout.test_app, null);
+        mTestCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+        switch (mTestCaseName) {
+            case Utils.LARGE_VIEW_HIERARCHY:
+                setContentView(R.layout.multiple_text_views);
+                return;
+            default:
+                setContentView(R.layout.test_app);
+        }
     }
 
     @Override
     public void onResume() {
         super.onResume();
+        Log.i(TAG, "TestApp has resumed");
+        final View layout = findViewById(android.R.id.content);
+        ViewTreeObserver vto = layout.getViewTreeObserver();
+        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+            @Override
+            public void onGlobalLayout() {
+                layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+            }
+        });
     }
 }
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java
new file mode 100644
index 0000000..9e57e9b
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.testapp;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import java.lang.Override;
+
+public class TextViewActivity extends Activity {
+    static final String TAG = "TextViewActivity";
+
+    private BroadcastReceiver mReceiver;
+    private TextView mTextView;
+    private ScrollView mScrollView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "TextViewActivity created");
+        setContentView(R.layout.text_view);
+        mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+        mTextView = (TextView) findViewById(R.id.text_view);
+        mTextView.setMovementMethod(new ScrollingMovementMethod());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.i(TAG, "TextViewActivity has resumed");
+
+        mReceiver = new ScrollReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Utils.SCROLL_TEXTVIEW_ACTION);
+        filter.addAction(Utils.SCROLL_SCROLLVIEW_ACTION);
+        registerReceiver(mReceiver, filter);
+
+        final View layout = findViewById(android.R.id.content);
+        ViewTreeObserver vto = layout.getViewTreeObserver();
+        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+            @Override
+            public void onGlobalLayout() {
+                layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+            }
+        });
+    }
+
+    @Override
+    public void onPause() {
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver);
+        }
+        super.onPause();
+    }
+
+    class ScrollReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int scrollX, scrollY;
+            scrollX = intent.getIntExtra(Utils.SCROLL_X_POSITION, 0);
+            scrollY = intent.getIntExtra(Utils.SCROLL_Y_POSITION, 0);
+            if (intent.getAction().equals(Utils.SCROLL_TEXTVIEW_ACTION)) {
+                Log.i(TAG, "Scrolling textview to (" + scrollX + "," + scrollY + ")");
+                if (scrollX < 0 || scrollY < 0) {
+                    // Scroll to bottom as negative positions are not possible.
+                    scrollX = mTextView.getWidth();
+                    scrollY = mTextView.getLayout().getLineTop(mTextView.getLineCount())
+                            - mTextView.getHeight();
+                }
+                TextViewActivity.this.mTextView.scrollTo(scrollX, scrollY);
+            } else if (intent.getAction().equals(Utils.SCROLL_SCROLLVIEW_ACTION)) {
+                Log.i(TAG, "Scrolling scrollview to (" + scrollX + "," + scrollY + ")");
+                if (scrollX < 0 || scrollY < 0) {
+                    // Scroll to bottom
+                    TextViewActivity.this.mScrollView.fullScroll(View.FOCUS_DOWN);
+                    TextViewActivity.this.mScrollView.fullScroll(View.FOCUS_RIGHT);
+                } else {
+                    TextViewActivity.this.mScrollView.scrollTo(scrollX, scrollY);
+                }
+            }
+            Log.i(TAG, "the max height of this textview is: " + mTextView.getHeight());
+            Log.i(TAG, "the max line count of this text view is: " + mTextView.getMaxLines());
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java
new file mode 100644
index 0000000..59f96cb
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.assist.testapp;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import java.lang.Override;
+
+public class WebViewActivity extends Activity {
+    static final String TAG = "WebViewActivity";
+
+    private String mTestCaseName;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "TestApp created");
+        mTestCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+        setContentView(R.layout.webview);
+        WebView webview = (WebView) findViewById(R.id.webview);
+        webview.setWebViewClient(new WebViewClient() {
+            @Override
+            public void onPageFinished(WebView view, String url){
+                sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+            }
+        });
+        webview.loadData(Utils.WEBVIEW_HTML, "text/html", "UTF-8");
+        //webview.loadUrl(
+        //        "https://android-developers.blogspot.com/2015/08/m-developer-preview-3-final-sdk.html");
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index a79df42..bc819c1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -41,6 +41,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -58,6 +59,7 @@
 
     private static final int SCAN_DURATION_MILLIS = 5000;
     private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 20000;
+    private CountDownLatch mFlushBatchScanLatch;
 
     private BluetoothAdapter mBluetoothAdapter;
     private BluetoothLeScanner mScanner;
@@ -215,8 +217,14 @@
                 batchScanCallback);
         sleep(SCAN_DURATION_MILLIS);
         mScanner.flushPendingScanResults(batchScanCallback);
-        sleep(1000);
+        mFlushBatchScanLatch = new CountDownLatch(1);
         List<ScanResult> results = batchScanCallback.getBatchScanResults();
+        try {
+            mFlushBatchScanLatch.await(5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            // Nothing to do.
+            Log.e(TAG, "interrupted!");
+        }
         assertTrue(!results.isEmpty());
         long scanEndMillis = SystemClock.elapsedRealtime();
         mScanner.stopScan(batchScanCallback);
@@ -249,7 +257,12 @@
 
         @Override
         public void onBatchScanResults(List<ScanResult> results) {
-            mBatchScanResults = results;
+            // In case onBatchScanResults are called due to buffer full, we want to collect all
+            // scan results.
+            mBatchScanResults.addAll(results);
+            if (mFlushBatchScanLatch != null) {
+                mFlushBatchScanLatch.countDown();
+            }
         }
 
         // Clear regular and batch scan results.
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index a20befb..bffb1f7 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -117,6 +117,14 @@
             </intent-filter>
         </receiver>
 
+        <!-- Receiver that will be explicitly disabled at runtime -->
+        <receiver android:name="android.content.cts.MockReceiverDisableable"
+                android:enabled="true">
+            <intent-filter android:priority="1">
+                <action android:name="android.content.cts.BroadcastReceiverTest.BROADCAST_DISABLED" />
+            </intent-filter>
+        </receiver>
+
         <activity android:name="android.content.cts.AvailableIntentsActivity"
             android:label="AvailableIntentsActivity">
             <intent-filter>
diff --git a/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java b/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java
index 526087b..43d6cfe 100644
--- a/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java
+++ b/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.test.ActivityInstrumentationTestCase2;
@@ -45,10 +46,16 @@
             "android.content.cts.BroadcastReceiverTest.BROADCAST_MOCKTEST";
     private static final String ACTION_BROADCAST_TESTABORT =
             "android.content.cts.BroadcastReceiverTest.BROADCAST_TESTABORT";
+    private static final String ACTION_BROADCAST_DISABLED =
+            "android.content.cts.BroadcastReceiverTest.BROADCAST_DISABLED";
 
     private static final long SEND_BROADCAST_TIMEOUT = 5000;
     private static final long START_SERVICE_TIMEOUT  = 3000;
 
+    private static final ComponentName DISABLEABLE_RECEIVER =
+            new ComponentName("com.android.cts.content",
+                    "android.content.cts.MockReceiverDisableable");
+
     public BroadcastReceiverTest() {
         super("com.android.cts.content", MockActivity.class);
     }
@@ -127,6 +134,26 @@
         }
     }
 
+    private class MockReceiverInternalVerifyUncalled extends MockReceiverInternal {
+        final int mExpectedInitialCode;
+
+        public MockReceiverInternalVerifyUncalled(int initialCode) {
+            mExpectedInitialCode = initialCode;
+        }
+
+        @Override
+        public synchronized void onReceive(Context context, Intent intent) {
+            // only update to the expected final values if we're still in the
+            // initial conditions.  The intermediate receiver would have
+            // updated the result code if it [inappropriately] ran.
+            if (getResultCode() == mExpectedInitialCode) {
+                setResultCode(RESULT_INTERNAL_FINAL_CODE);
+            }
+
+            super.onReceive(context, intent);
+        }
+    }
+
     public void testOnReceive () throws InterruptedException {
         final MockActivity activity = getActivity();
 
@@ -202,6 +229,26 @@
                 resultExtras.getString(MockReceiverAbort.RESULT_EXTRAS_ABORT_KEY));
     }
 
+    public void testDisabledBroadcastReceiver() throws Exception {
+        final Context context = getInstrumentation().getContext();
+        PackageManager pm = context.getPackageManager();
+
+        MockReceiverInternalVerifyUncalled lastReceiver =
+                new MockReceiverInternalVerifyUncalled(RESULT_INITIAL_CODE);
+        assertEquals(0, lastReceiver.getResultCode());
+
+        pm.setComponentEnabledSetting(DISABLEABLE_RECEIVER,
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+
+        context.sendOrderedBroadcast(
+                new Intent(ACTION_BROADCAST_DISABLED), null, lastReceiver,
+                null, RESULT_INITIAL_CODE, RESULT_INITIAL_DATA, new Bundle());
+        lastReceiver.waitForReceiver(SEND_BROADCAST_TIMEOUT);
+
+        assertEquals(RESULT_INTERNAL_FINAL_CODE, lastReceiver.getResultCode());
+    }
+
     public void testPeekService() throws InterruptedException {
         final MockActivity activity = getActivity();
 
diff --git a/tests/tests/content/src/android/content/cts/MockReceiverDisableable.java b/tests/tests/content/src/android/content/cts/MockReceiverDisableable.java
new file mode 100644
index 0000000..9ee73ed
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockReceiverDisableable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class MockReceiverDisableable extends BroadcastReceiver {
+    public static final int RESULT_CODE = 99;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        setResultCode(RESULT_CODE);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 0fef3d7..dd4e3e3 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -69,7 +69,8 @@
     private static final long EXPOSURE_TIME_BOUNDARY_50HZ_NS = 10000000L; // 10ms
     private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8333333L; // 8.3ms, Approximation.
     private static final long EXPOSURE_TIME_ERROR_MARGIN_NS = 100000L; // 100us, Approximation.
-    private static final int SENSITIVITY_ERROR_MARGIN = 10; // 10
+    private static final float EXPOSURE_TIME_ERROR_MARGIN_RATE = 0.03f; // 3%, Approximation.
+    private static final float SENSITIVITY_ERROR_MARGIN_RATE = 0.03f; // 3%, Approximation.
     private static final int DEFAULT_NUM_EXPOSURE_TIME_STEPS = 3;
     private static final int DEFAULT_NUM_SENSITIVITY_STEPS = 16;
     private static final int DEFAULT_SENSITIVITY_STEP_SIZE = 100;
@@ -107,6 +108,12 @@
     private final int INDEX_ALGORITHM_AWB = 1;
     private final int INDEX_ALGORITHM_AF = 2;
 
+    private enum TorchSeqState {
+        RAMPING_UP,
+        FIRED,
+        RAMPING_DOWN
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -963,16 +970,39 @@
         waitForNumResults(listener, flashModeTorchRequests - NUM_FLASH_REQUESTS_TESTED);
 
         // Verify the results
+        TorchSeqState state = TorchSeqState.RAMPING_UP;
         for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
             result = listener.getCaptureResultForRequest(torchRequest,
                     NUM_RESULTS_WAIT_TIMEOUT);
-
-            // Result mode must be TORCH, state must be FIRED
-            mCollector.expectEquals("Flash mode result must be TORCH",
+            int flashMode = result.get(CaptureResult.FLASH_MODE);
+            int flashState = result.get(CaptureResult.FLASH_STATE);
+            // Result mode must be TORCH
+            mCollector.expectEquals("Flash mode result " + i + " must be TORCH",
                     CaptureResult.FLASH_MODE_TORCH, result.get(CaptureResult.FLASH_MODE));
-            mCollector.expectEquals("Flash state result must be FIRED",
-                    CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+            if (state == TorchSeqState.RAMPING_UP &&
+                    flashState == CaptureResult.FLASH_STATE_FIRED) {
+                state = TorchSeqState.FIRED;
+            } else if (state == TorchSeqState.FIRED &&
+                    flashState == CaptureResult.FLASH_STATE_PARTIAL) {
+                state = TorchSeqState.RAMPING_DOWN;
+            }
+
+            if (i == 0 && mStaticInfo.isPerFrameControlSupported()) {
+                mCollector.expectTrue(
+                        "Per frame control device must enter FIRED state on first torch request",
+                        state == TorchSeqState.FIRED);
+            }
+
+            if (state == TorchSeqState.FIRED) {
+                mCollector.expectEquals("Flash state result " + i + " must be FIRED",
+                        CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+            } else {
+                mCollector.expectEquals("Flash state result " + i + " must be PARTIAL",
+                        CaptureResult.FLASH_STATE_PARTIAL, result.get(CaptureResult.FLASH_STATE));
+            }
         }
+        mCollector.expectTrue("Torch state FIRED never seen",
+                state == TorchSeqState.FIRED || state == TorchSeqState.RAMPING_DOWN);
 
         // Test flash OFF mode control
         requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
@@ -1995,12 +2025,12 @@
     private long[] getExposureTimeTestValues() {
         long[] testValues = new long[DEFAULT_NUM_EXPOSURE_TIME_STEPS + 1];
         long maxExpTime = mStaticInfo.getExposureMaximumOrDefault(DEFAULT_EXP_TIME_NS);
-        long minxExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
+        long minExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
 
-        long range = maxExpTime - minxExpTime;
+        long range = maxExpTime - minExpTime;
         double stepSize = range / (double)DEFAULT_NUM_EXPOSURE_TIME_STEPS;
         for (int i = 0; i < testValues.length; i++) {
-            testValues[i] = minxExpTime + (long)(stepSize * i);
+            testValues[i] = maxExpTime - (long)(stepSize * i);
             testValues[i] = mStaticInfo.getExposureClampToRange(testValues[i]);
         }
 
@@ -2049,7 +2079,7 @@
         }
         int[] testValues = new int[numSteps + 1];
         for (int i = 0; i < testValues.length; i++) {
-            testValues[i] = minSensitivity + stepSize * i;
+            testValues[i] = maxSensitivity - stepSize * i;
             testValues[i] = mStaticInfo.getSensitivityClampToRange(testValues[i]);
         }
 
@@ -2066,10 +2096,12 @@
      */
     private void validateExposureTime(long request, long result) {
         long expTimeDelta = request - result;
+        long expTimeErrorMargin = (long)(Math.max(EXPOSURE_TIME_ERROR_MARGIN_NS, request
+                * EXPOSURE_TIME_ERROR_MARGIN_RATE));
         // First, round down not up, second, need close enough.
         mCollector.expectTrue("Exposture time is invalid for AE manaul control test, request: "
                 + request + " result: " + result,
-                expTimeDelta < EXPOSURE_TIME_ERROR_MARGIN_NS && expTimeDelta >= 0);
+                expTimeDelta < expTimeErrorMargin && expTimeDelta >= 0);
     }
 
     /**
@@ -2079,11 +2111,12 @@
      * @param result Result sensitivity
      */
     private void validateSensitivity(int request, int result) {
-        int sensitivityDelta = request - result;
+        float sensitivityDelta = (float)(request - result);
+        float sensitivityErrorMargin = request * SENSITIVITY_ERROR_MARGIN_RATE;
         // First, round down not up, second, need close enough.
         mCollector.expectTrue("Sensitivity is invalid for AE manaul control test, request: "
                 + request + " result: " + result,
-                sensitivityDelta < SENSITIVITY_ERROR_MARGIN && sensitivityDelta >= 0);
+                sensitivityDelta < sensitivityErrorMargin && sensitivityDelta >= 0);
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
index b14c551..39eb1dc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -66,7 +66,7 @@
     private static final int NUM_MAX_IMAGES = 4;
     private static final int NUM_RESULTS_WAIT = 30;
     private static final int[] REPROCESS_FORMATS = {ImageFormat.YUV_420_888, ImageFormat.PRIVATE};
-    private final int MAX_REPROCESS_IMAGES = 10;
+    private final int MAX_REPROCESS_IMAGES = 6;
     private final int MAX_JPEG_IMAGES = MAX_REPROCESS_IMAGES;
     private final int MAX_INPUT_IMAGES = MAX_REPROCESS_IMAGES;
     // ZSL queue depth should be bigger than the max simultaneous reprocessing capture request
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index a6cf613..de0e3d5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -14,6 +14,7 @@
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 import static com.android.ex.camera2.blocking.BlockingSessionCallback.*;
 
+import android.cts.util.MediaUtils;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraCaptureSession;
@@ -151,6 +152,9 @@
      * </p>
      */
     public void testRecordingFromPersistentSurface() throws Exception {
+        if (!MediaUtils.checkCodecForDomain(true /* encoder */, "video")) {
+            return; // skipped
+        }
         mPersistentSurface = MediaCodec.createPersistentInputSurface();
         assertNotNull("Failed to create persistent input surface!", mPersistentSurface);
 
@@ -375,7 +379,7 @@
                     prepareRecording(size, videoFramerate, captureRate);
 
                     // prepare preview surface by using video size.
-                    updatePreviewSurfaceWithVideoSize(size);
+                    updatePreviewSurfaceWithVideo(size, captureRate);
 
                     // Start recording
                     SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -440,7 +444,7 @@
                         prepareRecording(size, VIDEO_FRAME_RATE, captureRate);
 
                         // prepare preview surface by using video size.
-                        updatePreviewSurfaceWithVideoSize(size);
+                        updatePreviewSurfaceWithVideo(size, captureRate);
 
                         // Start recording
                         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -631,7 +635,7 @@
             prepareRecordingWithProfile(profile);
 
             // prepare preview surface by using video size.
-            updatePreviewSurfaceWithVideoSize(videoSz);
+            updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
 
             // Start recording
             SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -681,7 +685,7 @@
             prepareRecording(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE);
 
             // prepare preview surface by using video size.
-            updatePreviewSurfaceWithVideoSize(sz);
+            updatePreviewSurfaceWithVideo(sz, VIDEO_FRAME_RATE);
 
             // Start recording
             SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -821,13 +825,21 @@
                     getAvailableMinFrameDurationsForFormatChecked(ImageFormat.JPEG);
             for (int i = mOrderedStillSizes.size() - 2; i >= 0; i--) {
                 Size candidateSize = mOrderedStillSizes.get(i);
-                Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
-                assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize,
-                        jpegFrameDuration != null);
-                if (candidateSize.getWidth() <= videoSz.getWidth() &&
-                        candidateSize.getHeight() <= videoSz.getHeight() &&
-                        jpegFrameDuration <= videoFrameDuration) {
-                    videoSnapshotSz = candidateSize;
+                if (mStaticInfo.isHardwareLevelLegacy()) {
+                    // Legacy level doesn't report min frame duration
+                    if (candidateSize.getWidth() <= videoSz.getWidth() &&
+                            candidateSize.getHeight() <= videoSz.getHeight()) {
+                        videoSnapshotSz = candidateSize;
+                    }
+                } else {
+                    Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
+                    assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize,
+                            jpegFrameDuration != null);
+                    if (candidateSize.getWidth() <= videoSz.getWidth() &&
+                            candidateSize.getHeight() <= videoSz.getHeight() &&
+                            jpegFrameDuration <= videoFrameDuration) {
+                        videoSnapshotSz = candidateSize;
+                    }
                 }
             }
 
@@ -893,7 +905,7 @@
                                 CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
 
                 // prepare preview surface by using video size.
-                updatePreviewSurfaceWithVideoSize(videoSz);
+                updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
 
                 prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
                 CaptureRequest request = videoSnapshotRequestBuilder.build();
@@ -993,20 +1005,44 @@
      * <p>Preview size will be capped with max preview size.</p>
      *
      * @param videoSize The video size used for preview.
+     * @param videoFrameRate The video frame rate
+     *
      */
-    private void updatePreviewSurfaceWithVideoSize(Size videoSize) {
+    private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) {
         if (mOrderedPreviewSizes == null) {
             throw new IllegalStateException("supported preview size list is not initialized yet");
         }
+        final float FRAME_DURATION_TOLERANCE = 0.01f;
+        long videoFrameDuration = (long) (1e9 / videoFrameRate *
+                (1.0 + FRAME_DURATION_TOLERANCE));
+        HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
+                getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE);
         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
-        Size previewSize = videoSize;
+        Size previewSize = null;
         if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
                 videoSize.getHeight() > maxPreviewSize.getHeight()) {
-            Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
-                    " to " + maxPreviewSize.toString());
-            previewSize = maxPreviewSize;
+            for (Size s : mOrderedPreviewSizes) {
+                Long frameDuration = minFrameDurationMap.get(s);
+                if (mStaticInfo.isHardwareLevelLegacy()) {
+                    // Legacy doesn't report min frame duration
+                    frameDuration = new Long(0);
+                }
+                assertTrue("Cannot find minimum frame duration for private size" + s,
+                        frameDuration != null);
+                if (frameDuration <= videoFrameDuration &&
+                        s.getWidth() <= videoSize.getWidth() &&
+                        s.getHeight() <= videoSize.getHeight()) {
+                    Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
+                            " to " + s.toString());
+                    previewSize = s;
+                    break;
+                    // If all preview size doesn't work then we fallback to video size
+                }
+            }
         }
-
+        if (previewSize == null) {
+            previewSize = videoSize;
+        }
         updatePreviewSurface(previewSize);
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 8a60dc2..dd49c8d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -1211,6 +1211,9 @@
             throw new IllegalArgumentException("isReprocessCaptures must have at least 1 capture.");
         }
 
+        boolean hasReprocessRequest = false;
+        boolean hasRegularRequest = false;
+
         TotalCaptureResult[] results = new TotalCaptureResult[isReprocessCaptures.length];
         for (int i = 0; i < isReprocessCaptures.length; i++) {
             // submit a capture and get the result if this entry is a reprocess capture.
@@ -1219,6 +1222,9 @@
                         /*inputResult*/null);
                 mImageWriter.queueInputImage(
                         mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
+                hasReprocessRequest = true;
+            } else {
+                hasRegularRequest = true;
             }
         }
 
@@ -1232,7 +1238,24 @@
         ImageResultHolder[] holders = new ImageResultHolder[isReprocessCaptures.length];
         for (int i = 0; i < isReprocessCaptures.length; i++) {
             Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS);
-            holders[i] = new ImageResultHolder(image, finalResults[i]);
+            if (hasReprocessRequest && hasRegularRequest) {
+                // If there are mixed requests, images and results may not be in the same order.
+                for (int j = 0; j < finalResults.length; j++) {
+                    if (finalResults[j] != null &&
+                            finalResults[j].get(CaptureResult.SENSOR_TIMESTAMP) ==
+                            image.getTimestamp()) {
+                        holders[i] = new ImageResultHolder(image, finalResults[j]);
+                        finalResults[j] = null;
+                        break;
+                    }
+                }
+
+                assertNotNull("Cannot find a result matching output image's timestamp: " +
+                        image.getTimestamp(), holders[i]);
+            } else {
+                // If no mixed requests, images and results should be in the same order.
+                holders[i] = new ImageResultHolder(image, finalResults[i]);
+            }
         }
 
         return holders;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 8ccc931..ddae8eb 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -1351,8 +1351,8 @@
         Log.i(TAG, String.format("Testing Camera %s, config %s",
                         cameraId, MaxStreamSizes.configToString(config)));
 
-        // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
-        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
+        // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
+        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
         final int MIN_RESULT_COUNT = 3;
 
         // Set up outputs
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
index 68efef0..a2ddb4d 100755
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -105,7 +105,7 @@
             if (supports64Bit) {
                 assertMinMemoryMb(1824);
             } else {
-                assertMinMemoryMb(1344);
+                assertMinMemoryMb(1099);
             }
         } else if (greaterThanDpi(density, DENSITY_400, screenSize,
                 SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
index 4750b09..22f092a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
@@ -35,21 +35,21 @@
  */
 public class SensorParameterRangeTest extends SensorTestCase {
 
-    private static final double ACCELEROMETER_MAX_RANGE = 8 * 9.81; // 8G
-    private static final int ACCELEROMETER_MIN_FREQUENCY = 5;
+    private static final double ACCELEROMETER_MAX_RANGE = 8 * 9.80; // 8G minus a slop
+    private static final double ACCELEROMETER_MIN_FREQUENCY = 12.50;
     private static final int ACCELEROMETER_MAX_FREQUENCY = 200;
 
-    private static final double GYRO_MAX_RANGE = 1000/57.295; // 1000 degrees per sec.
-    private static final int GYRO_MIN_FREQUENCY = 5;
-    private static final int GYRO_MAX_FREQUENCY = 200;
+    private static final double GYRO_MAX_RANGE = 1000/57.295 - 1.0; // 1000 degrees per sec minus a slop
+    private static final double GYRO_MIN_FREQUENCY = 12.50;
+    private static final double GYRO_MAX_FREQUENCY = 200.0;
 
     private static final int MAGNETOMETER_MAX_RANGE = 900;   // micro telsa
-    private static final int MAGNETOMETER_MIN_FREQUENCY = 5;
-    private static final int MAGNETOMETER_MAX_FREQUENCY = 50;
+    private static final double MAGNETOMETER_MIN_FREQUENCY = 5.0;
+    private static final double MAGNETOMETER_MAX_FREQUENCY = 50.0;
 
-    private static final int PRESSURE_MAX_RANGE = 1100;     // hecto-pascal
-    private static final int PRESSURE_MIN_FREQUENCY = 1;
-    private static final int PRESSURE_MAX_FREQUENCY = 10;
+    private static final double PRESSURE_MAX_RANGE = 1100.0;     // hecto-pascal
+    private static final double PRESSURE_MIN_FREQUENCY = 1.0;
+    private static final double PRESSURE_MAX_FREQUENCY = 10.0;
 
     private boolean mHasHifiSensors;
     private SensorManager mSensorManager;
@@ -94,7 +94,7 @@
     }
 
     private void checkSensorRangeAndFrequency(
-          Sensor sensor, double maxRange, int minFrequency, int maxFrequency) {
+          Sensor sensor, double maxRange, double minFrequency, double maxFrequency) {
         if (!mHasHifiSensors) return;
         assertTrue(String.format("%s Range actual=%.2f expected=%.2f %s",
                     sensor.getName(), sensor.getMaximumRange(), maxRange,
@@ -102,14 +102,14 @@
                 sensor.getMaximumRange() >= maxRange);
         double actualMinFrequency = SensorCtsHelper.getFrequency(sensor.getMaxDelay(),
                 TimeUnit.MICROSECONDS);
-        assertTrue(String.format("%s Min Frequency actual=%.2f expected=%dHz",
+        assertTrue(String.format("%s Min Frequency actual=%.2f expected=%.2fHz",
                     sensor.getName(), actualMinFrequency, minFrequency), actualMinFrequency <=
-                minFrequency);
+                minFrequency + 0.1);
 
         double actualMaxFrequency = SensorCtsHelper.getFrequency(sensor.getMinDelay(),
                 TimeUnit.MICROSECONDS);
-        assertTrue(String.format("%s Max Frequency actual=%.2f expected=%dHz",
+        assertTrue(String.format("%s Max Frequency actual=%.2f expected=%.2fHz",
                     sensor.getName(), actualMaxFrequency, maxFrequency), actualMaxFrequency >=
-                maxFrequency);
+                maxFrequency - 0.1);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 2c3c6f4..cffafdc 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -88,7 +88,7 @@
     }
 
     @Override
-    protected void tearDown(){
+    protected void tearDown() {
         if (mSensorManager != null) {
             // SensorManager will check listener and status, so just unregister listener
             mSensorManager.unregisterListener(mNullSensorEventListener);
@@ -256,7 +256,8 @@
         if (mTriggerSensor == null) {
             throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
-        boolean  result = mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+        boolean  result =
+            mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
         assertFalse(result);
     }
 
@@ -265,7 +266,8 @@
         if (mTriggerSensor == null) {
             throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
-        boolean result = mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+        boolean result =
+            mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
         assertFalse(result);
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 55465ac..9cb3710 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -38,21 +38,32 @@
     private SensorCtsHelper() {}
 
     /**
-     * Get the value of the 95th percentile using nearest rank algorithm.
+     * Get percentiles using nearest rank algorithm.
      *
-     * @throws IllegalArgumentException if the collection is null or empty
+     * @param percentiles List of percentiles interested. Its range is 0 to 1, instead of in %.
+     *        The value will be internally bounded.
+     *
+     * @throws IllegalArgumentException if the collection or percentiles is null or empty
      */
-    public static <TValue extends Comparable<? super TValue>> TValue get95PercentileValue(
-            Collection<TValue> collection) {
+    public static <TValue extends Comparable<? super TValue>> List<TValue> getPercentileValue(
+            Collection<TValue> collection, float[] percentiles) {
         validateCollection(collection);
+        if(percentiles == null || percentiles.length == 0) {
+            throw new IllegalStateException("percentiles cannot be null or empty");
+        }
 
         List<TValue> arrayCopy = new ArrayList<TValue>(collection);
         Collections.sort(arrayCopy);
 
-        // zero-based array index
-        int arrayIndex = (int) Math.round(arrayCopy.size() * 0.95 + .5) - 1;
-
-        return arrayCopy.get(arrayIndex);
+        List<TValue> percentileValues = new ArrayList<TValue>();
+        for (float p : percentiles) {
+            // zero-based array index
+            int arrayIndex = (int) Math.round(arrayCopy.size() * p - .5f);
+            // bound the index to avoid out of range error
+            arrayIndex = Math.min(Math.max(arrayIndex, 0), arrayCopy.size() - 1);
+            percentileValues.add(arrayCopy.get(arrayIndex));
+        }
+        return percentileValues;
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 6156d3d..47c8313 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -223,7 +223,7 @@
         if (mSamplingPeriodUs == SensorManager.SENSOR_DELAY_FASTEST) {
             return "fastest";
         }
-        return String.format("%.0fhz", getFrequencyHz());
+        return String.format("%.2fhz", getFrequencyHz());
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 3e70e75..e8df1ab 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -172,8 +172,7 @@
      */
     public List<TestSensorEvent> getCollectedEvents() {
         synchronized (mCollectedEvents){
-            List<TestSensorEvent> collectedEventsList = (List)mCollectedEvents.clone();
-            return Collections.unmodifiableList(collectedEventsList);
+            return Collections.unmodifiableList((List<TestSensorEvent>) mCollectedEvents.clone());
         }
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
index cf34f28..2f4777b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
@@ -128,9 +128,9 @@
         stats.addValue(SensorStats.FREQUENCY_KEY, measuredFrequencyHz);
         stats.addValue(PASSED_KEY, !failed);
         String resultString = String.format(
-                "Requested \"%s\" at %.2fHz (expecting between %.2fHz and %.2fHz, measured %.2fHz)",
+                "Requested \"%s\" at %s (expecting between %.2fHz and %.2fHz, measured %.2fHz)",
                 environment.getSensor().getName(),
-                environment.getFrequencyHz(),
+                environment.getFrequencyString(),
                 mLowerThresholdHz,
                 mUpperThresholdHz,
                 measuredFrequencyHz);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
index d246ec5..9d36f37 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
@@ -16,8 +16,6 @@
 
 package android.hardware.cts.helpers.sensorverification;
 
-import junit.framework.Assert;
-
 import android.content.Context;
 import android.content.pm.PackageManager;
 
@@ -34,6 +32,7 @@
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import junit.framework.Assert;
 
 /**
  * A {@link ISensorVerification} which verifies that the sensor jitter is in an acceptable range.
@@ -43,15 +42,22 @@
 
     // sensorType: threshold (% of expected period)
     private static final SparseIntArray DEFAULTS = new SparseIntArray(12);
-    // Max allowed jitter (in percentage).
+    // Max allowed jitter in +/- sense (in percentage).
     private static final int GRACE_FACTOR = 2;
     private static final int THRESHOLD_PERCENT_FOR_HIFI_SENSORS = 1 * GRACE_FACTOR;
+
+    // Margin sample intervals that considered outliers, lower and higher margin is discarded
+    // before verification
+    private static final float OUTLIER_MARGIN = 0.025f; //2.5%
+
     static {
         // Use a method so that the @deprecation warning can be set for that method only
         setDefaults();
     }
 
-    private final int mThresholdAsPercentage;
+    private final float     mOutlierMargin;
+    private final long      mThresholdNs;
+    private final long      mExpectedPeriodNs; // for error message only
     private final List<Long> mTimestamps = new LinkedList<Long>();
 
     /**
@@ -59,8 +65,10 @@
      *
      * @param thresholdAsPercentage the acceptable margin of error as a percentage
      */
-    public JitterVerification(int thresholdAsPercentage) {
-        mThresholdAsPercentage = thresholdAsPercentage;
+    public JitterVerification(float outlierMargin, long thresholdNs, long expectedPeriodNs) {
+        mExpectedPeriodNs = expectedPeriodNs;
+        mOutlierMargin = outlierMargin;
+        mThresholdNs = thresholdNs;
     }
 
     /**
@@ -71,16 +79,20 @@
      */
     public static JitterVerification getDefault(TestSensorEnvironment environment) {
         int sensorType = environment.getSensor().getType();
-        int threshold = DEFAULTS.get(sensorType, -1);
-        if (threshold == -1) {
+
+        int thresholdPercent = DEFAULTS.get(sensorType, -1);
+        if (thresholdPercent == -1) {
             return null;
         }
         boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_HIFI_SENSORS);
         if (hasHifiSensors) {
-           threshold = THRESHOLD_PERCENT_FOR_HIFI_SENSORS;
+           thresholdPercent = THRESHOLD_PERCENT_FOR_HIFI_SENSORS;
         }
-        return new JitterVerification(threshold);
+
+        long expectedPeriodNs = (long) environment.getExpectedSamplingPeriodUs() * 1000;
+        long jitterThresholdNs = expectedPeriodNs * thresholdPercent * 2 / 100; // *2 is for +/-
+        return new JitterVerification(OUTLIER_MARGIN, jitterThresholdNs, expectedPeriodNs);
     }
 
     /**
@@ -99,24 +111,33 @@
             return;
         }
 
-        List<Double> jitters = getJitterValues();
-        double jitter95PercentileNs = SensorCtsHelper.get95PercentileValue(jitters);
-        long firstTimestamp = mTimestamps.get(0);
-        long lastTimestamp = mTimestamps.get(timestampsCount - 1);
-        long measuredPeriodNs = (lastTimestamp - firstTimestamp) / (timestampsCount - 1);
-        double jitter95PercentilePercent = (jitter95PercentileNs * 100.0) / measuredPeriodNs;
-        stats.addValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY, jitter95PercentilePercent);
+        List<Long> deltas = getDeltaValues();
+        float percentiles[] = new float[2];
+        percentiles[0] = mOutlierMargin;
+        percentiles[1] = 1 - percentiles[0];
 
-        boolean success = (jitter95PercentilePercent < mThresholdAsPercentage);
+        List<Long> percentileValues = SensorCtsHelper.getPercentileValue(deltas, percentiles);
+        double normalizedRange =
+                (double)(percentileValues.get(1) - percentileValues.get(0)) / mThresholdNs;
+
+        double percentageJitter =
+                (double)(percentileValues.get(1) - percentileValues.get(0)) /
+                        mExpectedPeriodNs / 2 * 100; //one side variation comparing to sample time
+
+        stats.addValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY, percentageJitter);
+
+        boolean success = normalizedRange <= 1.0;
         stats.addValue(PASSED_KEY, success);
 
         if (!success) {
             String message = String.format(
-                    "Jitter out of range: measured period=%dns, jitter(95th percentile)=%.2f%%"
-                            + " (expected < %d%%)",
-                    measuredPeriodNs,
-                    jitter95PercentilePercent,
-                    mThresholdAsPercentage);
+                    "Jitter out of range: requested period = %dns, " +
+                    "jitter min, max, range (95th percentile) = (%dns, %dns, %dns), " +
+                    "jitter expected range <= %dns",
+                    mExpectedPeriodNs,
+                    percentileValues.get(0), percentileValues.get(1),
+                    percentileValues.get(1) - percentileValues.get(0),
+                    mThresholdNs);
             Assert.fail(message);
         }
     }
@@ -126,7 +147,7 @@
      */
     @Override
     public JitterVerification clone() {
-        return new JitterVerification(mThresholdAsPercentage);
+        return new JitterVerification(mOutlierMargin, mThresholdNs, mExpectedPeriodNs);
     }
 
     /**
@@ -138,19 +159,14 @@
     }
 
     /**
-     * Get the list of all jitter values. Exposed for unit testing.
+     * Get the list of delta values. Exposed for unit testing.
      */
-    List<Double> getJitterValues() {
+    List<Long> getDeltaValues() {
         List<Long> deltas = new ArrayList<Long>(mTimestamps.size() - 1);
         for (int i = 1; i < mTimestamps.size(); i++) {
             deltas.add(mTimestamps.get(i) - mTimestamps.get(i - 1));
         }
-        double deltaMean = StatisticsUtils.getMean(deltas);
-        List<Double> jitters = new ArrayList<Double>(deltas.size());
-        for (long delta : deltas) {
-            jitters.add(Math.abs(delta - deltaMean));
-        }
-        return jitters;
+        return deltas;
     }
 
     @SuppressWarnings("deprecation")
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
index 50e288c..d8e1586 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
@@ -27,11 +27,10 @@
 import java.util.List;
 
 /**
- * Tests for {@link EventOrderingVerification}.
+ * Tests for {@link JitterVerification}.
  */
 public class JitterVerificationTest extends TestCase {
 
-
     public void testVerify() {
         final int SAMPLE_SIZE = 100;
         // for unit testing the verification, only the parameter 'sensorMightHaveMoreListeners' is
@@ -67,54 +66,58 @@
         } catch (AssertionError e) {
             // Expected;
         }
-        verifyStats(stats, false, 47.34);
+        verifyStats(stats, false, 25); // 500 us range (250 us in single-sided sense)
+                                       // divide by 1ms requested sample time x 100%
     }
 
-    public void testCalculateJitter() {
+    public void testCalculateDelta() {
         long[] timestamps = new long[]{0, 1, 2, 3, 4};
         JitterVerification verification = getVerification(1, timestamps);
-        List<Double> jitterValues = verification.getJitterValues();
-        assertEquals(4, jitterValues.size());
-        assertEquals(0.0, jitterValues.get(0));
-        assertEquals(0.0, jitterValues.get(1));
-        assertEquals(0.0, jitterValues.get(2));
-        assertEquals(0.0, jitterValues.get(3));
+        List<Long> deltaValues = verification.getDeltaValues();
+        assertEquals(4, deltaValues.size());
+        assertEquals(1, deltaValues.get(0).longValue());
+        assertEquals(1, deltaValues.get(1).longValue());
+        assertEquals(1, deltaValues.get(2).longValue());
+        assertEquals(1, deltaValues.get(3).longValue());
 
         timestamps = new long[]{0, 0, 2, 4, 4};
         verification = getVerification(1, timestamps);
-        jitterValues = verification.getJitterValues();
-        assertEquals(4, jitterValues.size());
-        assertEquals(1.0, jitterValues.get(0));
-        assertEquals(1.0, jitterValues.get(1));
-        assertEquals(1.0, jitterValues.get(2));
-        assertEquals(1.0, jitterValues.get(3));
+        deltaValues = verification.getDeltaValues();
+        assertEquals(4, deltaValues.size());
+        assertEquals(0, deltaValues.get(0).longValue());
+        assertEquals(2, deltaValues.get(1).longValue());
+        assertEquals(2, deltaValues.get(2).longValue());
+        assertEquals(0, deltaValues.get(3).longValue());
 
         timestamps = new long[]{0, 1, 4, 9, 16};
         verification = getVerification(1, timestamps);
-        jitterValues = verification.getJitterValues();
-        assertEquals(4, jitterValues.size());
-        assertEquals(4, jitterValues.size());
-        assertEquals(3.0, jitterValues.get(0));
-        assertEquals(1.0, jitterValues.get(1));
-        assertEquals(1.0, jitterValues.get(2));
-        assertEquals(3.0, jitterValues.get(3));
+        deltaValues = verification.getDeltaValues();
+        assertEquals(4, deltaValues.size());
+        assertEquals(1, deltaValues.get(0).longValue());
+        assertEquals(3, deltaValues.get(1).longValue());
+        assertEquals(5, deltaValues.get(2).longValue());
+        assertEquals(7, deltaValues.get(3).longValue());
     }
 
-    private static JitterVerification getVerification(int threshold, long ... timestamps) {
+    private static JitterVerification getVerification(int marginPercent, long ... timestamps) {
         Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
         for (long timestamp : timestamps) {
             events.add(new TestSensorEvent(null, timestamp, 0, null));
         }
-        JitterVerification verification = new JitterVerification(threshold);
+        long samplePeriodNs = 1000*1000; //1000Hz
+        long jitterThresholdNs = 20*1000; // 2% of that
+
+        JitterVerification verification =
+                new JitterVerification(marginPercent/100.0f, jitterThresholdNs, samplePeriodNs);
         verification.addSensorEvents(events);
         return verification;
     }
 
-    private void verifyStats(SensorStats stats, boolean passed, double jitter95) {
+    private void verifyStats(SensorStats stats, boolean passed, double percentageJitter) {
         assertEquals(passed, stats.getValue(JitterVerification.PASSED_KEY));
         assertEquals(
-                jitter95,
+                percentageJitter,
                 (Double) stats.getValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY),
-                0.1);
+                0.01);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
index 1e1c950..1b66e6a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
@@ -24,8 +24,10 @@
 import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.SensorCtsHelper;
 import android.util.Log;
 
+import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -66,18 +68,37 @@
      */
     public static StandardDeviationVerification getDefault(TestSensorEnvironment environment) {
         int sensorType = environment.getSensor().getType();
-        float mGraceFactorAccelGyro = 2.0f;
-        float mGraceFactorMagPressure = 4.0f;
-        float mMaxBandWidth = (float) environment.getFrequencyHz();
-        float mAccelNoise = (float)(mGraceFactorAccelGyro * Math.sqrt(mMaxBandWidth) * (9.81 * 0.0004));
-        float mGyroNoise = (float)(mGraceFactorAccelGyro * Math.sqrt(mMaxBandWidth) * (Math.PI/180.0 * 0.014));
-        float mMagNoise = (float)((mGraceFactorMagPressure) * 0.5); // Allow extra grace for mag
-        float mPressureNoise = (float)(mGraceFactorMagPressure * 0.02 * (float)Math.sqrt(mMaxBandWidth)); // Allow extra grace for pressure
+        float graceFactorAccelGyro = 2.0f;
+        float graceFactorMagPressure = 4.0f;
+        float currOperatingFreq = (float) environment.getFrequencyHz();
+        float maxBandWidth = (float)SensorCtsHelper.getFrequency(
+                environment.getSensor().getMinDelay(), TimeUnit.MICROSECONDS);
+        float minBandWidth = (float) SensorCtsHelper.getFrequency(
+                environment.getSensor().getMaxDelay(), TimeUnit.MICROSECONDS);
+
+        if (Float.isInfinite(currOperatingFreq)) {
+            currOperatingFreq = maxBandWidth;
+        }
+
+        if (currOperatingFreq > maxBandWidth && !Float.isInfinite(maxBandWidth)) {
+            currOperatingFreq = maxBandWidth;
+        }
+
+        if (currOperatingFreq < minBandWidth && !Float.isInfinite(minBandWidth)) {
+            currOperatingFreq = minBandWidth;
+        }
+
+        float mAccelNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
+                (9.81 * 0.0004));
+        float mGyroNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
+                (Math.PI/180.0 * 0.014));
+        float mMagNoise = (float)((graceFactorMagPressure) * 0.5); // Allow extra grace for mag
+        float mPressureNoise = (float)(graceFactorMagPressure * 0.02 *
+                (float)Math.sqrt(currOperatingFreq)); // Allow extra grace for pressure
 
         if (!DEFAULTS.containsKey(sensorType)) {
             return null;
         }
-
         boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_HIFI_SENSORS);
 
@@ -132,9 +153,9 @@
             if (stdDevs[i] > mThreshold[i]) {
                 failed = true;
             }
-            stddevSb.append(String.format("%.3f", stdDevs[i]));
+            stddevSb.append(String.format("%.6f", stdDevs[i]));
             if (i != stdDevs.length - 1) stddevSb.append(", ");
-            expectedSb.append(String.format("<%.3f", mThreshold[i]));
+            expectedSb.append(String.format("<%.6f", mThreshold[i]));
             if (i != stdDevs.length - 1) expectedSb.append(", ");
         }
         if (stdDevs.length > 1) {
diff --git a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index aa34de3..d1ca19a 100644
--- a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -16,6 +16,7 @@
 
 package android.hardware.multiprocess.camera.cts;
 
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
@@ -112,7 +113,8 @@
         super.setUp();
 
         mCompleted = false;
-        mContext = getActivity();
+        getActivity();
+        mContext = getInstrumentation().getTargetContext();
         System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         mErrorServiceConnection = new ErrorLoggingService.ErrorServiceConnection(mContext);
@@ -232,6 +234,7 @@
         assertTrue("Remote camera service exited early", timeoutExceptionHit);
         android.os.Process.killProcess(mProcessPid);
         mProcessPid = -1;
+        forceCtsActivityToTop();
     }
 
     /**
@@ -337,6 +340,19 @@
         assertTrue("Remote camera service exited early", timeoutExceptionHit);
         android.os.Process.killProcess(mProcessPid);
         mProcessPid = -1;
+        forceCtsActivityToTop();
+    }
+
+    /**
+     * Ensure the CTS activity becomes foreground again instead of launcher.
+     */
+    private void forceCtsActivityToTop() throws InterruptedException {
+        Thread.sleep(WAIT_TIME);
+        Activity a = getActivity();
+        Intent activityIntent = new Intent(a, CameraCtsActivity.class);
+        activityIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        a.startActivity(activityIntent);
+        Thread.sleep(WAIT_TIME);
     }
 
     /**
@@ -389,15 +405,15 @@
     public void startRemoteProcess(java.lang.Class<?> klass, String processName)
             throws InterruptedException {
         // Ensure no running activity process with same name
-        String cameraActivityName = mContext.getPackageName() + ":" + processName;
+        Activity a = getActivity();
+        String cameraActivityName = a.getPackageName() + ":" + processName;
         List<ActivityManager.RunningAppProcessInfo> list =
                 mActivityManager.getRunningAppProcesses();
         assertEquals(-1, getPid(cameraActivityName, list));
 
         // Start activity in a new top foreground process
-        Intent activityIntent = new Intent(mContext, klass);
-        activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(activityIntent);
+        Intent activityIntent = new Intent(a, klass);
+        a.startActivity(activityIntent);
         Thread.sleep(WAIT_TIME);
 
         // Fail if activity isn't running
diff --git a/tests/tests/media/res/raw/heap_oob_flac.mp3 b/tests/tests/media/res/raw/heap_oob_flac.mp3
new file mode 100644
index 0000000..ae542d0
--- /dev/null
+++ b/tests/tests/media/res/raw/heap_oob_flac.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4 b/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4
new file mode 100644
index 0000000..110c0d6
--- /dev/null
+++ b/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/test1m1shighstereo.mp3 b/tests/tests/media/res/raw/test1m1shighstereo.mp3
new file mode 100644
index 0000000..2a97077
--- /dev/null
+++ b/tests/tests/media/res/raw/test1m1shighstereo.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4 b/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4
old mode 100644
new mode 100755
index b837dc2..f88fe7a
--- a/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4
+++ b/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 4c03183..9ae6b64 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1646,6 +1646,7 @@
                     Thread.sleep(WAIT_MSEC); // wait for the data to drain.
                     // -------- tear down --------------
                     track.release();
+                    Thread.sleep(WAIT_MSEC); // wait for release to complete
                     frequency += 50; // increment test tone frequency
                 }
             }
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 9da229c..8650d20 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -20,6 +20,8 @@
 import android.content.res.AssetFileDescriptor;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
@@ -1141,4 +1143,87 @@
     private static String getMimeTypeFor(MediaFormat format) {
         return format.getString(MediaFormat.KEY_MIME);
     }
+
+    /**
+     * Returns the first codec capable of encoding the specified MIME type, or null if no match was
+     * found.
+     */
+    private static MediaCodecInfo selectCodec(String mimeType) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+            if (!codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; j++) {
+                if (types[j].equalsIgnoreCase(mimeType)) {
+                    return codecInfo;
+                }
+            }
+        }
+        return null;
+    }
+
+  /**
+   * Checks whether the given resolution is supported by the AVC codec.
+   */
+    private static boolean isAvcSupportedSize(int width, int height) {
+        MediaCodecInfo mediaCodecInfo = selectCodec(OUTPUT_VIDEO_MIME_TYPE);
+        CodecCapabilities cap = mediaCodecInfo.getCapabilitiesForType(OUTPUT_VIDEO_MIME_TYPE);
+        if (cap == null) { // not supported
+            return false;
+        }
+        int highestLevel = 0;
+        for (CodecProfileLevel lvl : cap.profileLevels) {
+            if (lvl.level > highestLevel) {
+                highestLevel = lvl.level;
+            }
+        }
+        int maxW = 0;
+        int maxH = 0;
+        int bitRate = 0;
+        int fps = 0; // frame rate for the max resolution
+        switch(highestLevel) {
+            // Do not support Level 1 to 2.
+            case CodecProfileLevel.AVCLevel1:
+            case CodecProfileLevel.AVCLevel11:
+            case CodecProfileLevel.AVCLevel12:
+            case CodecProfileLevel.AVCLevel13:
+            case CodecProfileLevel.AVCLevel1b:
+            case CodecProfileLevel.AVCLevel2:
+                return false;
+            case CodecProfileLevel.AVCLevel21:
+                maxW = 352;
+                maxH = 576;
+                break;
+            case CodecProfileLevel.AVCLevel22:
+                maxW = 720;
+                maxH = 480;
+                break;
+            case CodecProfileLevel.AVCLevel3:
+                maxW = 720;
+                maxH = 480;
+                break;
+            case CodecProfileLevel.AVCLevel31:
+                maxW = 1280;
+                maxH = 720;
+                break;
+            case CodecProfileLevel.AVCLevel32:
+                maxW = 1280;
+                maxH = 720;
+                break;
+            case CodecProfileLevel.AVCLevel4: // only try up to 1080p
+            default:
+                maxW = 1920;
+                maxH = 1080;
+                break;
+        }
+        if(maxW*maxH < width*height)
+            return false;
+        else
+            return true;
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/JetPlayerTest.java b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
index fc03bcc..4df3555 100644
--- a/tests/tests/media/src/android/media/cts/JetPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
@@ -55,6 +55,10 @@
 
     @Override
     protected void tearDown() throws Exception {
+        // Prevent tests from failing with EAS_ERROR_FILE_ALREADY_OPEN
+        // after a previous test fails.
+        mJetPlayer.closeJetFile();
+
         File jetFile = new File(mJetFile);
         if (jetFile.exists()) {
             jetFile.delete();
@@ -63,31 +67,32 @@
     }
 
     public void testLoadJetFromPath() throws Throwable {
-        mJetPlayer.clearQueue();
+        assertTrue(mJetPlayer.clearQueue());
         prepareFile();
         mJetPlayer.setEventListener(mOnJetEventListener);
-        mJetPlayer.loadJetFile(mJetFile);
+        assertTrue(mJetPlayer.loadJetFile(mJetFile));
         runJet();
     }
 
     public void testLoadJetFromFd() throws Throwable {
-        mJetPlayer.clearQueue();
+        assertTrue(mJetPlayer.clearQueue());
         mJetPlayer.setEventListener(mOnJetEventListener, mHandler);
-        mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet));
+        assertTrue(mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet)));
         runJet();
     }
 
     public void testQueueJetSegmentMuteArray() throws Throwable {
-        mJetPlayer.clearQueue();
+        assertTrue(mJetPlayer.clearQueue());
         mJetPlayer.setEventListener(mOnJetEventListener, mHandler);
-        mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet));
+        assertTrue(mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet)));
         byte userID = 0;
         int segmentNum = 3;
         int libNum = -1;
         int repeatCount = 0;
         int transpose = 0;
         boolean[] muteFlags = new boolean[32];
-        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum, repeatCount, transpose,
+        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum,
+                repeatCount, transpose,
                 muteFlags, userID));
         assertTrue(mJetPlayer.play());
         for (int i = 0; i < muteFlags.length; i++) {
@@ -96,7 +101,8 @@
         muteFlags[8] = false;
         muteFlags[9] = false;
         muteFlags[10] = false;
-        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum, repeatCount, transpose,
+        assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum,
+                repeatCount, transpose,
                 muteFlags, userID));
         Thread.sleep(20000);
         assertTrue(mJetPlayer.pause());
@@ -112,16 +118,19 @@
         int repeatCount = 1;
         int transpose = 0;
         int muteFlags = 0;
-        mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+        assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+                transpose, muteFlags, userID));
 
         segmentNum = 6;
         repeatCount = 1;
         transpose = -1;
-        mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+        assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+                transpose, muteFlags, userID));
 
         segmentNum = 7;
         transpose = 0;
-        mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+        assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+                transpose, muteFlags, userID));
 
         for (int i = 0; i < 7; i++) {
             assertTrue(mJetPlayer.triggerClip(i));
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 813af0f2..71cbd61 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -600,6 +600,29 @@
         return actualMax;
     }
 
+    private boolean knownTypes(String type) {
+        return (type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC  ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC3      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB   ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB   ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM    ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS   ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC     ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2    ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4    ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8      ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9      ));
+    }
+
     public void testGetMaxSupportedInstances() {
         final int MAX_INSTANCES = 32;
         StringBuilder xmlOverrides = new StringBuilder();
@@ -610,6 +633,10 @@
 
             String[] types = info.getSupportedTypes();
             for (int j = 0; j < types.length; ++j) {
+                if (!knownTypes(types[j])) {
+                    Log.d(TAG, "skipping unknown type " + types[j]);
+                    continue;
+                }
                 Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
                 CodecCapabilities caps = info.getCapabilitiesForType(types[j]);
                 int max = caps.getMaxSupportedInstances();
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 86f0313..75a5a13 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.cts.util.MediaUtils;
+import android.hardware.Camera;
 import android.media.AudioManager;
 import android.media.MediaCodec;
 import android.media.MediaDataSource;
@@ -49,6 +50,7 @@
 import java.io.File;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.UUID;
 import java.util.Vector;
@@ -95,6 +97,49 @@
         }
     }
 
+    public void testonInputBufferFilledSigsegv() throws Exception {
+        testIfMediaServerDied(R.raw.on_input_buffer_filled_sigsegv);
+    }
+
+    public void testFlacHeapOverflow() throws Exception {
+        testIfMediaServerDied(R.raw.heap_oob_flac);
+    }
+
+    private void testIfMediaServerDied(int res) throws Exception {
+        mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+            @Override
+            public boolean onError(MediaPlayer mp, int what, int extra) {
+                assertTrue(mp == mMediaPlayer);
+                assertTrue("mediaserver process died", what != MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+                Log.w(LOG_TAG, "onError " + what);
+                return false;
+            }
+        });
+
+        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                assertTrue(mp == mMediaPlayer);
+                mOnCompletionCalled.signal();
+            }
+        });
+
+        AssetFileDescriptor afd = mResources.openRawResourceFd(res);
+        mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        afd.close();
+        try {
+            mMediaPlayer.prepare();
+            mMediaPlayer.start();
+            if (!mOnCompletionCalled.waitForSignal(5000)) {
+                Log.w(LOG_TAG, "testIfMediaServerDied: Timed out waiting for Error/Completion");
+            }
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "playback failed", e);
+        } finally {
+            mMediaPlayer.release();
+        }
+    }
+
     // Bug 13652927
     public void testVorbisCrash() throws Exception {
         MediaPlayer mp = mMediaPlayer;
@@ -700,7 +745,7 @@
     public void testVideoSurfaceResetting() throws Exception {
         final int tolerance = 150;
         final int audioLatencyTolerance = 1000;  /* covers audio path latency variability */
-        final int seekPos = 5000;
+        final int seekPos = 4760;  // This is the I-frame position
 
         final CountDownLatch seekDone = new CountDownLatch(1);
 
@@ -722,7 +767,12 @@
         mMediaPlayer.setDisplay(getActivity().getSurfaceHolder2());
         int posAfter = mMediaPlayer.getCurrentPosition();
 
-        assertEquals(posAfter, posBefore, tolerance);
+        /* temporarily disable timestamp checking because MediaPlayer now seeks to I-frame
+         * position, instead of requested position. setDisplay invovles a seek operation
+         * internally.
+         */
+        // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+        // assertEquals(posAfter, posBefore, tolerance);
         assertTrue(mMediaPlayer.isPlaying());
 
         Thread.sleep(SLEEP_TIME);
@@ -736,7 +786,8 @@
         posBefore = mMediaPlayer.getCurrentPosition();
         mMediaPlayer.setDisplay(null);
         posAfter = mMediaPlayer.getCurrentPosition();
-        assertEquals(posAfter, posBefore, tolerance);
+        // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+        // assertEquals(posAfter, posBefore, tolerance);
         assertTrue(mMediaPlayer.isPlaying());
 
         Thread.sleep(SLEEP_TIME);
@@ -745,7 +796,8 @@
         mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
         posAfter = mMediaPlayer.getCurrentPosition();
 
-        assertEquals(posAfter, posBefore, tolerance);
+        // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+        // assertEquals(posAfter, posBefore, tolerance);
         assertTrue(mMediaPlayer.isPlaying());
 
         Thread.sleep(SLEEP_TIME);
@@ -771,15 +823,34 @@
         return getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
     }
 
+    private Camera mCamera;
     private void testRecordedVideoPlaybackWithAngle(int angle) throws Exception {
-        final int width = RECORDED_VIDEO_WIDTH;
-        final int height = RECORDED_VIDEO_HEIGHT;
+        int width = RECORDED_VIDEO_WIDTH;
+        int height = RECORDED_VIDEO_HEIGHT;
         final String file = RECORDED_FILE;
         final long durationMs = RECORDED_DURATION_MS;
 
         if (!hasCamera()) {
             return;
         }
+
+        boolean isSupported = false;
+        mCamera = Camera.open(0);
+        Camera.Parameters parameters = mCamera.getParameters();
+        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+        for (Camera.Size size : previewSizes)
+        {
+            if (size.width == width && size.height == height) {
+                isSupported = true;
+                break;
+            }
+        }
+        mCamera.release();
+        mCamera = null;
+        if (!isSupported) {
+            width = previewSizes.get(0).width;
+            height = previewSizes.get(0).height;
+        }
         checkOrientation(angle);
         recordVideo(width, height, angle, file, durationMs);
         checkDisplayedVideoSize(width, height, angle, file);
@@ -1403,6 +1474,42 @@
         return 1;
     }
 
+    public void testPositionAtEnd() throws Throwable {
+        testPositionAtEnd(R.raw.test1m1shighstereo);
+        testPositionAtEnd(R.raw.loudsoftmp3);
+        testPositionAtEnd(R.raw.loudsoftmp3);
+        testPositionAtEnd(R.raw.loudsoftwav);
+        testPositionAtEnd(R.raw.loudsoftogg);
+        testPositionAtEnd(R.raw.loudsoftitunes);
+        testPositionAtEnd(R.raw.loudsoftfaac);
+        testPositionAtEnd(R.raw.loudsoftaac);
+    }
+
+    private void testPositionAtEnd(int res) throws Throwable {
+
+        loadResource(res);
+        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+        mMediaPlayer.prepare();
+        int duration = mMediaPlayer.getDuration();
+        assertTrue("resource too short", duration > 6000);
+        mOnCompletionCalled.reset();
+        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                mOnCompletionCalled.signal();
+            }
+        });
+        mMediaPlayer.seekTo(duration - 5000);
+        mMediaPlayer.start();
+        while (mMediaPlayer.isPlaying()) {
+            Log.i("@@@@", "position: " + mMediaPlayer.getCurrentPosition());
+            Thread.sleep(500);
+        }
+        Log.i("@@@@", "final position: " + mMediaPlayer.getCurrentPosition());
+        assertTrue(mMediaPlayer.getCurrentPosition() > duration - 1000);
+        mMediaPlayer.reset();
+    }
+
     public void testCallback() throws Throwable {
         final int mp4Duration = 8484;
 
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index f7b6c91..4c90e56 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -60,6 +60,8 @@
     private static final int RECORDED_DUR_TOLERANCE_MS = 1000;
     private static final int VIDEO_WIDTH = 176;
     private static final int VIDEO_HEIGHT = 144;
+    private static int mVideoWidth = VIDEO_WIDTH;
+    private static int mVideoHeight = VIDEO_HEIGHT;
     private static final int VIDEO_BIT_RATE_IN_BPS = 128000;
     private static final double VIDEO_TIMELAPSE_CAPTURE_RATE_FPS = 1.0;
     private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
@@ -219,6 +221,7 @@
         int durMs = timelapse? RECORD_TIME_LAPSE_MS: RECORD_TIME_MS;
         for (int cameraId = 0; cameraId < nCamera; cameraId++) {
             mCamera = Camera.open(cameraId);
+            setSupportedResolution(mCamera);
             recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse);
             mCamera.release();
             mCamera = null;
@@ -226,6 +229,21 @@
         }
     }
 
+    private void setSupportedResolution(Camera camera) {
+        Camera.Parameters parameters = camera.getParameters();
+        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+        for (Camera.Size size : previewSizes)
+        {
+            if (size.width == VIDEO_WIDTH && size.height == VIDEO_HEIGHT) {
+                mVideoWidth = VIDEO_WIDTH;
+                mVideoHeight = VIDEO_HEIGHT;
+                return;
+            }
+        }
+        mVideoWidth = previewSizes.get(0).width;
+        mVideoHeight = previewSizes.get(0).height;
+    }
+
     private void recordVideoUsingCamera(
             Camera camera, String fileName, int durMs, boolean timelapse) throws Exception {
         // FIXME:
@@ -242,7 +260,7 @@
         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
         mMediaRecorder.setVideoFrameRate(frameRate);
-        mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
+        mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
         mMediaRecorder.setOutputFile(fileName);
         mMediaRecorder.setLocation(LATITUDE, LONGITUDE);
@@ -872,6 +890,12 @@
         boolean success = false;
         Surface surface = null;
         int noOfFailure = 0;
+
+        if (!hasH264()) {
+            MediaUtils.skipTest("no codecs");
+            return true;
+        }
+
         try {
             if (persistent) {
                 surface = MediaCodec.createPersistentInputSurface();
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 7497da2..ce61d76 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -312,6 +312,11 @@
     }
 
     public void testPlayHlsStreamWithTimedId3() throws Throwable {
+        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+            Log.d(TAG, "Device doesn't have video codec, skipping test");
+            return;
+        }
+
         mServer = new CtsTestServer(mContext);
         try {
             // counter must be final if we want to access it inside onTimedMetaData;
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index e3d2f09..fb1521d 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -174,6 +174,7 @@
         private boolean mSignaledDecoderEOS;
 
         protected boolean mCompleted;
+        protected boolean mEncoderIsActive;
         protected boolean mEncodeOutputFormatUpdated;
         protected final Object mCondition = new Object();
 
@@ -322,6 +323,10 @@
                         mCompleted = true;
                         mCondition.notifyAll(); // condition is always satisfied
                     }
+                } else {
+                    synchronized(mCondition) {
+                        mEncoderIsActive = true;
+                    }
                 }
             }
         }
@@ -395,6 +400,11 @@
                             break;
                         }
                         if (!haveBuffers()) {
+                            if (mEncoderIsActive) {
+                                mEncoderIsActive = false;
+                                Log.d(TAG, "No more input but still getting output from encoder.");
+                                continue;
+                            }
                             fail("timed out after " + mBuffersToRender.size()
                                     + " decoder output and " + mEncInputBuffers.size()
                                     + " encoder input buffers");
@@ -557,7 +567,6 @@
             implements SurfaceTexture.OnFrameAvailableListener {
         private static final String TAG = "SurfaceVideoProcessor";
         private boolean mFrameAvailable;
-        private boolean mEncoderIsActive;
         private boolean mGotDecoderEOS;
         private boolean mSignaledEncoderEOS;
 
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index 88dbd7c..9a99c22 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,6 +16,9 @@
 
 package android.net.cts;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -23,6 +26,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
@@ -467,4 +471,23 @@
             mAvailableLatch.countDown();
         }
     }
+
+    /** Verify restricted networks cannot be requested. */
+    public void testRestrictedNetworks() {
+        // Verify we can request unrestricted networks:
+        NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET).build();
+        NetworkCallback callback = new NetworkCallback();
+        mCm.requestNetwork(request, callback);
+        mCm.unregisterNetworkCallback(callback);
+        // Verify we cannot request restricted networks:
+        request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build();
+        callback = new NetworkCallback();
+        try {
+            mCm.requestNetwork(request, callback);
+            fail("No exception thrown when restricted network requested.");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+    }
 }
diff --git a/tests/tests/os/jni/seccomp_sample_program.cpp b/tests/tests/os/jni/seccomp_sample_program.cpp
index 3c90196..3bc7da4 100644
--- a/tests/tests/os/jni/seccomp_sample_program.cpp
+++ b/tests/tests/os/jni/seccomp_sample_program.cpp
@@ -826,7 +826,7 @@
   {0x35, 0, 4, 0x76},
   {0x35, 0, 2, 0x79},
   {0x35, 0, 241, 0x7a},
-  {0x35, 240, 239, 0x7b},
+  {0x35, 240, 239, 0x7c},
   {0x35, 238, 239, 0x77},
   {0x35, 0, 2, 0x72},
   {0x35, 0, 236, 0x73},
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 419f320..af60139 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -29,8 +29,8 @@
 
     private static final String LOG_TAG = "BuildVersionTest";
     private static final Set<String> EXPECTED_RELEASES =
-            new HashSet<String>(Arrays.asList("5.1", "5.1.1"));
-    private static final int EXPECTED_SDK = 22;
+            new HashSet<String>(Arrays.asList("6.0"));
+    private static final int EXPECTED_SDK = 23;
     private static final String EXPECTED_BUILD_VARIANT = "user";
     private static final String EXPECTED_TAG = "release-keys";
 
diff --git a/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
index 0b389a4..d8e128b 100644
--- a/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
+++ b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
@@ -82,8 +82,8 @@
     }
 
     private static final String[] armv8RequiredFeatures = {
-            "wp", "half", "thumb", "fastmult", "vfp", "edsp", "neon",
-            "vfpv3", "tlsi", "vfpv4", "idiva", "idivt" };
+            "half", "thumb", "fastmult", "vfp", "edsp", "neon",
+            "vfpv3", "vfpv4", "idiva", "idivt" };
 
     private static void assertInCpuinfo(List<String> features,
             String feature) {
diff --git a/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
index 8714100..a95c96e 100644
--- a/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
@@ -53,21 +53,4 @@
             // expected
         }
     }
-
-    /**
-     * Verify that calling {@link ConnectivityManager#requestRouteToHost(int, int)}
-     * requires permissions.
-     * <p>Tests Permission:
-     *   {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
-     */
-    @SmallTest
-    public void testRequestRouteToHost() {
-        try {
-            mConnectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, 1);
-            fail("Was able to call requestRouteToHost");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
 }
-
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index cf6a09d..0962bbd 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -251,7 +251,6 @@
     @MediumTest
     public void testProcSelfOomAdjSane() {
         File f = new File("/proc/self/oom_adj");
-        assertTrue(f.canRead());
         assertFalse(f.canWrite());
         assertFalse(f.canExecute());
     }
@@ -259,7 +258,6 @@
     @MediumTest
     public void testProcSelfOomScoreAdjSane() {
         File f = new File("/proc/self/oom_score_adj");
-        assertTrue(f.canRead());
         assertFalse(f.canWrite());
         assertFalse(f.canExecute());
     }
diff --git a/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java b/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
index 0eae9cc..35ebdc1 100644
--- a/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
@@ -86,38 +86,6 @@
         }
     }
 
-    /**
-     * Verify that ConnectivityManager#startUsingNetworkFeature() requires permissions.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
-     */
-    @SmallTest
-    public void testStartUsingNetworkFeature() {
-        try {
-            mConnectivityManager.startUsingNetworkFeature(TEST_NETWORK_TYPE, TEST_FEATURE);
-            fail("ConnectivityManager.startUsingNetworkFeature didn't throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    /**
-     * Verify that ConnectivityManager#requestRouteToHost() requires permissions.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
-     */
-    @SmallTest
-    public void testRequestRouteToHost() {
-        try {
-            mConnectivityManager.requestRouteToHost(TEST_NETWORK_TYPE, 0xffffffff);
-            fail("ConnectivityManager.requestRouteToHost didn't throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
     @SmallTest
     public void testSecurityExceptionFromDns() throws Exception {
         try {
diff --git a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
index f7e5443..c260706 100644
--- a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
@@ -98,4 +98,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
index e75ec94..1378bdb 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -140,6 +140,11 @@
 
     @Override
     public void setUp() throws Exception {
+        super.setUp();
+        if (!supportsPrinting()) {
+            return;
+        }
+
         // Make sure we start with a clean slate.
         clearPrintSpoolerData();
         enablePrintServices();
@@ -176,23 +181,26 @@
 
     @Override
     public void tearDown() throws Exception {
-        // Done with the activity.
-        getActivity().finish();
-        enableImes();
+        if (supportsPrinting()) {
+            // Done with the activity.
+            getActivity().finish();
+            enableImes();
 
-        // Restore the locale if needed.
-        if (mOldLocale != null) {
-            Resources resources = getInstrumentation().getTargetContext().getResources();
-            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
-            Configuration newConfiguration = new Configuration(resources.getConfiguration());
-            newConfiguration.locale = mOldLocale;
-            mOldLocale = null;
-            resources.updateConfiguration(newConfiguration, displayMetrics);
+            // Restore the locale if needed.
+            if (mOldLocale != null) {
+                Resources resources = getInstrumentation().getTargetContext().getResources();
+                DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+                Configuration newConfiguration = new Configuration(resources.getConfiguration());
+                newConfiguration.locale = mOldLocale;
+                mOldLocale = null;
+                resources.updateConfiguration(newConfiguration, displayMetrics);
+            }
+
+            disablePrintServices();
+            // Make sure the spooler is cleaned.
+            clearPrintSpoolerData();
         }
-
-        disablePrintServices();
-        // Make sure the spooler is cleaned.
-        clearPrintSpoolerData();
+        super.tearDown();
     }
 
     protected void print(final PrintDocumentAdapter adapter) {
diff --git a/tests/tests/systemui/Android.mk b/tests/tests/systemui/Android.mk
new file mode 100644
index 0000000..1a15fd2
--- /dev/null
+++ b/tests/tests/systemui/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSystemUiTestCases
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml
new file mode 100644
index 0000000..bf5df5b
--- /dev/null
+++ b/tests/tests/systemui/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.systemui">
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application>
+        <activity android:name=".LightStatusBarActivity"
+                android:theme="@android:style/Theme.Material.NoActionBar"></activity>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.systemui">
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
new file mode 100644
index 0000000..626a179
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.systemui;
+
+/**
+ * Copies of non-public {@link android.graphics.Color} APIs
+ */
+public class ColorUtils {
+
+    public static float brightness(int argb) {
+        int r = (argb >> 16) & 0xFF;
+        int g = (argb >> 8) & 0xFF;
+        int b = argb & 0xFF;
+
+        int V = Math.max(b, Math.max(r, g));
+
+        return (V / 255.f);
+    }
+
+    public static float hue(int argb) {
+        int r = (argb >> 16) & 0xFF;
+        int g = (argb >> 8) & 0xFF;
+        int b = argb & 0xFF;
+
+        int V = Math.max(b, Math.max(r, g));
+        int temp = Math.min(b, Math.min(r, g));
+
+        float H;
+
+        if (V == temp) {
+            H = 0;
+        } else {
+            final float vtemp = (float) (V - temp);
+            final float cr = (V - r) / vtemp;
+            final float cg = (V - g) / vtemp;
+            final float cb = (V - b) / vtemp;
+
+            if (r == V) {
+                H = cb - cg;
+            } else if (g == V) {
+                H = 2 + cr - cb;
+            } else {
+                H = 4 + cg - cr;
+            }
+
+            H /= 6.f;
+            if (H < 0) {
+                H++;
+            }
+        }
+
+        return H;
+    }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
new file mode 100644
index 0000000..3722320
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.cts.systemui;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+
+/**
+ * An activity that exercises SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ */
+public class LightStatusBarActivity extends Activity {
+
+    private View mContent;
+
+    public void onCreate(Bundle bundle){
+        super.onCreate(bundle);
+
+        mContent = new View(this);
+        mContent.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+                LayoutParams.MATCH_PARENT));
+        setContentView(mContent);
+    }
+
+    public void setLightStatusBar(boolean lightStatusBar) {
+        int vis = getWindow().getDecorView().getSystemUiVisibility();
+        if (lightStatusBar) {
+            vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        } else {
+            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        }
+        getWindow().getDecorView().setSystemUiVisibility(vis);
+    }
+
+    public int getTop() {
+        return mContent.getLocationOnScreen()[1];
+    }
+
+    public int getWidth() {
+        return mContent.getWidth();
+    }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
new file mode 100644
index 0000000..b5bfd51
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.systemui;
+
+import android.app.ActivityManager;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Test for light status bar.
+ */
+public class LightStatusBarTests extends ActivityInstrumentationTestCase2<LightStatusBarActivity> {
+
+    public static final String TAG = "LightStatusBarTests";
+
+    public static final String DUMP_PATH = "/sdcard/lightstatustest.png";
+
+    public LightStatusBarTests() {
+        super(LightStatusBarActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // As the way to access Instrumentation is changed in the new runner, we need to inject it
+        // manually into ActivityInstrumentationTestCase2. ActivityInstrumentationTestCase2 will
+        // be marked as deprecated and replaced with ActivityTestRule.
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+    }
+
+    public void testLightStatusBarIcons() throws Throwable {
+        PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+                || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+            // No status bar on TVs and watches.
+            return;
+        }
+
+        if (!ActivityManager.isHighEndGfx()) {
+            // non-highEndGfx devices don't do colored system bars.
+            return;
+        }
+
+        requestLightStatusBar(Color.RED /* background */);
+        Thread.sleep(1000);
+
+        Bitmap bitmap = takeStatusBarScreenshot();
+        Stats s = evaluateLightStatusBarBitmap(bitmap, Color.RED /* background */);
+        boolean success = false;
+
+        try {
+            assertMoreThan("Not enough background pixels", 0.3f,
+                    (float) s.backgroundPixels / s.totalPixels(),
+                    "Is the status bar background showing correctly (solid red)?");
+
+            assertMoreThan("Not enough pixels colored as in the spec", 0.1f,
+                    (float) s.iconPixels / s.foregroundPixels(),
+                    "Are the status bar icons colored according to the spec "
+                            + "(60% black and 24% black)?");
+
+            assertLessThan("Too many lighter pixels lighter than the background", 0.05f,
+                    (float) s.sameHueLightPixels / s.foregroundPixels(),
+                    "Are the status bar icons dark?");
+
+            assertLessThan("Too many pixels with a changed hue", 0.05f,
+                    (float) s.unexpectedHuePixels / s.foregroundPixels(),
+                    "Are the status bar icons color-free?");
+
+            success = true;
+        } finally {
+            if (!success) {
+                Log.e(TAG, "Dumping failed bitmap to " + DUMP_PATH);
+                dumpBitmap(bitmap);
+            }
+        }
+    }
+
+    private void assertMoreThan(String what, float expected, float actual, String hint) {
+        if (!(actual > expected)) {
+            fail(what + ": expected more than " + expected * 100 + "%, but only got " + actual * 100
+                    + "%; " + hint);
+        }
+    }
+
+    private void assertLessThan(String what, float expected, float actual, String hint) {
+        if (!(actual < expected)) {
+            fail(what + ": expected less than " + expected * 100 + "%, but got " + actual * 100
+                    + "%; " + hint);
+        }
+    }
+
+    private void requestLightStatusBar(final int background) throws Throwable {
+        final LightStatusBarActivity activity = getActivity();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.getWindow().setStatusBarColor(background);
+                activity.setLightStatusBar(true);
+            }
+        });
+    }
+
+    private static class Stats {
+        int backgroundPixels;
+        int iconPixels;
+        int sameHueDarkPixels;
+        int sameHueLightPixels;
+        int unexpectedHuePixels;
+
+        int totalPixels() {
+            return backgroundPixels + iconPixels + sameHueDarkPixels
+                    + sameHueLightPixels + unexpectedHuePixels;
+        }
+
+        int foregroundPixels() {
+            return iconPixels + sameHueDarkPixels
+                    + sameHueLightPixels + unexpectedHuePixels;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{bg=%d, ic=%d, dark=%d, light=%d, bad=%d}",
+                    backgroundPixels, iconPixels, sameHueDarkPixels, sameHueLightPixels,
+                    unexpectedHuePixels);
+        }
+    }
+
+    private Stats evaluateLightStatusBarBitmap(Bitmap bitmap, int background) {
+        int iconColor = 0x99000000;
+        int iconPartialColor = 0x3d000000;
+
+        int mixedIconColor = mixSrcOver(background, iconColor);
+        int mixedIconPartialColor = mixSrcOver(background, iconPartialColor);
+
+        int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
+        bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+        Stats s = new Stats();
+        float eps = 0.005f;
+
+        for (int c : pixels) {
+            if (c == background) {
+                s.backgroundPixels++;
+                continue;
+            }
+
+            // What we expect the icons to be colored according to the spec.
+            if (c == mixedIconColor || c == mixedIconPartialColor) {
+                s.iconPixels++;
+                continue;
+            }
+
+            // Due to anti-aliasing, there will be deviations from the ideal icon color, but it
+            // should still be mostly the same hue.
+            float hueDiff = Math.abs(ColorUtils.hue(background) - ColorUtils.hue(c));
+            if (hueDiff < eps || hueDiff > 1 - eps) {
+                // .. it shouldn't be lighter than the original background though.
+                if (ColorUtils.brightness(c) > ColorUtils.brightness(background)) {
+                    s.sameHueLightPixels++;
+                } else {
+                    s.sameHueDarkPixels++;
+                }
+                continue;
+            }
+
+            s.unexpectedHuePixels++;
+        }
+
+        return s;
+    }
+
+    private void dumpBitmap(Bitmap bitmap) {
+        FileOutputStream fileStream = null;
+        try {
+            fileStream = new FileOutputStream(DUMP_PATH);
+            bitmap.compress(Bitmap.CompressFormat.PNG, 85, fileStream);
+            fileStream.flush();
+        } catch (Exception e) {
+            Log.e(TAG, "Dumping bitmap failed.", e);
+        } finally {
+            if (fileStream != null) {
+                try {
+                    fileStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private int mixSrcOver(int background, int foreground) {
+        int bgAlpha = Color.alpha(background);
+        int bgRed = Color.red(background);
+        int bgGreen = Color.green(background);
+        int bgBlue = Color.blue(background);
+
+        int fgAlpha = Color.alpha(foreground);
+        int fgRed = Color.red(foreground);
+        int fgGreen = Color.green(foreground);
+        int fgBlue = Color.blue(foreground);
+
+        return Color.argb(fgAlpha + (255 - fgAlpha) * bgAlpha / 255,
+                    fgRed + (255 - fgAlpha) * bgRed / 255,
+                    fgGreen + (255 - fgAlpha) * bgGreen / 255,
+                    fgBlue + (255 - fgAlpha) * bgBlue / 255);
+    }
+
+    private Bitmap takeStatusBarScreenshot() {
+        Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
+        return Bitmap.createBitmap(fullBitmap, 0, 0,
+                getActivity().getWidth(), getActivity().getTop());
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java b/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java
index 52c8317..3a0dbd3 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java
@@ -51,16 +51,21 @@
 
     @Override
     protected void tearDown() throws Exception {
-        tearDownConnectionServices(TEST_PHONE_ACCOUNT_HANDLE, TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
+        if (mShouldTestTelecom) {
+            tearDownRemoteConnectionService(TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
+        }
         super.tearDown();
     }
 
     protected void setupConnectionServices(MockConnectionService connectionService,
-            MockConnectionService remoteConnectionService, int flags)
-            throws Exception {
+            MockConnectionService remoteConnectionService, int flags) throws Exception {
         // Setup the primary connection service first
         setupConnectionService(connectionService, flags);
+        setupRemoteConnectionService(remoteConnectionService, flags);
+    }
 
+    protected void setupRemoteConnectionService(MockConnectionService remoteConnectionService,
+            int flags) throws Exception {
         if (remoteConnectionService != null) {
             this.remoteConnectionService = remoteConnectionService;
         } else {
@@ -76,19 +81,23 @@
                     TEST_REMOTE_PHONE_ACCOUNT_HANDLE,
                     REMOTE_ACCOUNT_LABEL,
                     TEST_REMOTE_PHONE_ACCOUNT_ADDRESS);
+            // Wait till the adb commands have executed and account is in Telecom database.
+            assertPhoneAccountRegistered(TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
         }
         if ((flags & FLAG_ENABLE) != 0) {
             TestUtils.enablePhoneAccount(getInstrumentation(), TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
+            // Wait till the adb commands have executed and account is enabled in Telecom database.
+            assertPhoneAccountEnabled(TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
         }
     }
 
-    protected void tearDownConnectionServices(PhoneAccountHandle accountHandle,
-            PhoneAccountHandle remoteAccountHandle) throws Exception {
-        // Teardown the primary connection service first
-        tearDownConnectionService(accountHandle);
-
+    protected void tearDownRemoteConnectionService(PhoneAccountHandle remoteAccountHandle)
+            throws Exception {
+        assertNumConnections(this.remoteConnectionService, 0);
         mTelecomManager.unregisterPhoneAccount(remoteAccountHandle);
         CtsRemoteConnectionService.tearDown();
+        //Telecom doesn't unbind the remote connection service at the end of all calls today.
+        //assertCtsRemoteConnectionServiceUnbound();
         this.remoteConnectionService = null;
     }
 
@@ -208,4 +217,22 @@
                 "Remote Conference should be in state " + state
         );
     }
+
+    void assertCtsRemoteConnectionServiceUnbound() {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected(){
+                        return true;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return CtsRemoteConnectionService.isServiceUnbound();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "CtsRemoteConnectionService not yet unbound!"
+        );
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 31ca09b..d7fe239 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -110,13 +110,13 @@
                 TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
             }
             tearDownConnectionService(TEST_PHONE_ACCOUNT_HANDLE);
+            assertMockInCallServiceUnbound();
         }
         super.tearDown();
     }
 
     protected PhoneAccount setupConnectionService(MockConnectionService connectionService,
-            int flags)
-            throws Exception {
+            int flags) throws Exception {
         if (connectionService != null) {
             this.connectionService = connectionService;
         } else {
@@ -130,14 +130,18 @@
         }
         if ((flags & FLAG_ENABLE) != 0) {
             TestUtils.enablePhoneAccount(getInstrumentation(), TEST_PHONE_ACCOUNT_HANDLE);
+            // Wait till the adb commands have executed and account is enabled in Telecom database.
+            assertPhoneAccountEnabled(TEST_PHONE_ACCOUNT_HANDLE);
         }
 
         return TEST_PHONE_ACCOUNT;
     }
 
     protected void tearDownConnectionService(PhoneAccountHandle accountHandle) throws Exception {
+        assertNumConnections(this.connectionService, 0);
         mTelecomManager.unregisterPhoneAccount(accountHandle);
         CtsConnectionService.tearDown();
+        assertCtsConnectionServiceUnbound();
         this.connectionService = null;
     }
 
@@ -513,6 +517,22 @@
     }
 
 
+    void assertNumConnections(final MockConnectionService connService, final int numConnections) {
+        waitUntilConditionIsTrueOrTimeout(new Condition() {
+                                              @Override
+                                              public Object expected() {
+                                                  return numConnections;
+                                              }
+                                              @Override
+                                              public Object actual() {
+                                                  return connService.getAllConnections().size();
+                                              }
+                                          },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "ConnectionService should contain " + numConnections + " connections."
+        );
+    }
+
     void assertMuteState(final InCallService incallService, final boolean isMuted) {
         waitUntilConditionIsTrueOrTimeout(
                 new Condition() {
@@ -621,7 +641,7 @@
                     }
                 },
                 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-                "Call should be in state " + state
+                "Call: " + call + " should be in state " + state
         );
     }
 
@@ -731,6 +751,79 @@
         );
     }
 
+    void assertPhoneAccountRegistered(final PhoneAccountHandle handle) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return true;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return mTelecomManager.getPhoneAccount(handle) != null;
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Phone account registration failed for " + handle
+        );
+    }
+
+    void assertPhoneAccountEnabled(final PhoneAccountHandle handle) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return true;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle);
+                        return (phoneAccount != null && phoneAccount.isEnabled());
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Phone account enable failed for " + handle
+        );
+    }
+
+    void assertCtsConnectionServiceUnbound() {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return true;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return CtsConnectionService.isServiceUnbound();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "CtsConnectionService not yet unbound!"
+        );
+    }
+
+    void assertMockInCallServiceUnbound() {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return true;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return MockInCallService.isServiceUnbound();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "MockInCallService not yet unbound!"
+        );
+    }
+
     void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout,
             String description) {
         final long start = System.currentTimeMillis();
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
index 121d559..508870c 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
@@ -50,7 +50,6 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mContext = getInstrumentation().getContext();
         if (mShouldTestTelecom) {
             addOutgoingCalls();
             addConferenceCall(mCall1, mCall2);
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
index afaa3eb..836ca48 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
@@ -48,20 +48,13 @@
         placeAndVerifyCall();
         verifyConnectionForOutgoingCall();
 
+        // Add second connection (add existing connection)
         final MockConnection connection = new MockConnection();
         connection.setOnHold();
         CtsConnectionService.addExistingConnectionToTelecom(TEST_PHONE_ACCOUNT_HANDLE, connection);
-
-        try {
-            if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
-                fail("No call added to InCallService.");
-            }
-        } catch (InterruptedException e) {
-            Log.i(TAG, "Test interrupted!");
-        }
-
-        final MockInCallService inCallService = mInCallCallbacks.getService();
-        final Call call = inCallService.getLastCall();
+        assertNumCalls(mInCallCallbacks.getService(), 2);
+        mInCallCallbacks.lock.drainPermits();
+        final Call call = mInCallCallbacks.getService().getLastCall();
         assertCallState(call, Call.STATE_HOLDING);
     }
 
@@ -77,17 +70,22 @@
         Collection<Connection> connections = CtsConnectionService.getAllConnectionsFromTelecom();
         assertEquals(1, connections.size());
         assertTrue(connections.contains(connection1));
+        // Need to move this to active since we reject the 3rd incoming call below if this is in
+        // dialing state (b/23428950).
+        connection1.setActive();
+        assertCallState(mInCallCallbacks.getService().getLastCall(), Call.STATE_ACTIVE);
 
         // Add second connection (add existing connection)
         final Connection connection2 = new MockConnection();
         CtsConnectionService.addExistingConnectionToTelecom(TEST_PHONE_ACCOUNT_HANDLE, connection2);
-
+        assertNumCalls(mInCallCallbacks.getService(), 2);
+        mInCallCallbacks.lock.drainPermits();
         connections = CtsConnectionService.getAllConnectionsFromTelecom();
         assertEquals(2, connections.size());
         assertTrue(connections.contains(connection2));
 
         // Add third connection (incoming call)
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         final Connection connection3 = verifyConnectionForIncomingCall();
         connections = CtsConnectionService.getAllConnectionsFromTelecom();
         assertEquals(3, connections.size());
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
index 3c30e6b..d8d5773 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
@@ -16,6 +16,9 @@
 
 package android.telecom.cts;
 
+import static org.junit.Assert.assertFalse;
+
+import android.content.Intent;
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
@@ -23,6 +26,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.RemoteConference;
 import android.telecom.RemoteConnection;
+import android.util.Log;
 
 import java.util.Collection;
 
@@ -43,10 +47,12 @@
  *
  */
 public class CtsConnectionService extends ConnectionService {
+    private static String LOG_TAG = "CtsConnectionService";
     // This is the connection service implemented by the test
     private static ConnectionService sConnectionService;
     // This is the connection service registered with Telecom
     private static ConnectionService sTelecomConnectionService;
+    private static boolean mIsServiceUnbound;
 
     public CtsConnectionService() throws Exception {
         super();
@@ -65,13 +71,14 @@
     private static Object sLock = new Object();
 
     public static void setUp(PhoneAccountHandle phoneAccountHandle,
-            ConnectionService connectionService)
-            throws Exception {
+            ConnectionService connectionService) throws Exception {
         synchronized(sLock) {
             if (sConnectionService != null) {
                 throw new Exception("Mock ConnectionService exists.  Failed to call tearDown().");
             }
             sConnectionService = connectionService;
+            // Cant override the onBind method for ConnectionService, so reset it here.
+            mIsServiceUnbound = false;
         }
     }
 
@@ -178,4 +185,16 @@
             }
         }
     }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Log.i(LOG_TAG, "Service unbounded");
+        assertFalse(mIsServiceUnbound);
+        mIsServiceUnbound = true;
+        return super.onUnbind(intent);
+    }
+
+    public static boolean isServiceUnbound() {
+        return mIsServiceUnbound;
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java
index 3738487..13b525f 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java
@@ -16,11 +16,15 @@
 
 package android.telecom.cts;
 
+import static org.junit.Assert.assertFalse;
+
+import android.content.Intent;
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
 import android.telecom.ConnectionService;
 import android.telecom.PhoneAccountHandle;
+import android.util.Log;
 
 /**
  * This is the Remote ConnectionService for Telecom's CTS App. Since telecom requires that a
@@ -39,10 +43,12 @@
  *
  */
 public class CtsRemoteConnectionService extends ConnectionService {
+    private static String LOG_TAG = "CtsConnectionService";
     // This is the connection service implemented by the test
     private static ConnectionService sConnectionService;
     // This is the connection service registered with Telecom
     private static ConnectionService sTelecomConnectionService;
+    private static boolean mIsServiceUnbound;
 
     public CtsRemoteConnectionService() throws Exception {
         super();
@@ -64,13 +70,14 @@
     private static Object sLock = new Object();
 
     public static void setUp(PhoneAccountHandle phoneAccountHandle,
-            ConnectionService connectionService)
-            throws Exception {
+            ConnectionService connectionService) throws Exception {
         synchronized(sLock) {
             if (sConnectionService != null) {
                 throw new Exception("Mock ConnectionService exists.  Failed to call tearDown().");
             }
             sConnectionService = connectionService;
+            // Cant override the onBind method for ConnectionService, so reset it here.
+            mIsServiceUnbound = false;
         }
     }
 
@@ -124,4 +131,16 @@
             sTelecomConnectionService.addConference(conference);
         }
     }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Log.i(LOG_TAG, "Service unbounded");
+        assertFalse(mIsServiceUnbound);
+        mIsServiceUnbound = true;
+        return super.onUnbind(intent);
+    }
+
+    public static boolean isServiceUnbound() {
+        return mIsServiceUnbound;
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index 19d27af..02d2f15 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -188,7 +188,7 @@
             return;
         }
 
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         final MockConnection connection = verifyConnectionForIncomingCall();
 
         final MockInCallService inCallService = mInCallCallbacks.getService();
@@ -209,7 +209,7 @@
             return;
         }
 
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         final MockConnection connection = verifyConnectionForIncomingCall();
 
         final MockInCallService inCallService = mInCallCallbacks.getService();
@@ -232,7 +232,7 @@
             return;
         }
 
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         final MockConnection connection = verifyConnectionForIncomingCall();
 
         final MockInCallService inCallService = mInCallCallbacks.getService();
@@ -390,7 +390,7 @@
             return;
         }
 
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         verifyConnectionForIncomingCall();
         final MockInCallService inCallService = mInCallCallbacks.getService();
 
@@ -428,7 +428,7 @@
         assertEquals("InCallService.getCalls() should return list with 1 call.", 1, calls.size());
         assertEquals(call1, calls.get(0));
 
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         verifyConnectionForIncomingCall();
 
         final Call call2 = inCallService.getLastCall();
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
index d556baa..9bb83a1 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
@@ -66,6 +66,7 @@
         if (mRemoteConnection != null) {
             mRemoteConnection.reject();
         }
+        destroy();
     }
 
     @Override
@@ -90,18 +91,20 @@
     public void onDisconnect() {
         super.onDisconnect();
         setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
-        destroy();
         if (mRemoteConnection != null) {
             mRemoteConnection.disconnect();
         }
+        destroy();
     }
 
     @Override
     public void onAbort() {
         super.onAbort();
+        setDisconnected(new DisconnectCause(DisconnectCause.UNKNOWN));
         if (mRemoteConnection != null) {
             mRemoteConnection.abort();
         }
+        destroy();
     }
 
     @Override
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index 1856603..0f1f538 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -16,10 +16,14 @@
 
 package android.telecom.cts;
 
+import static org.junit.Assert.assertFalse;
+
+import android.content.Intent;
 import android.telecom.Call;
 import android.telecom.CallAudioState;
 import android.telecom.InCallService;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -27,6 +31,7 @@
 import java.util.concurrent.Semaphore;
 
 public class MockInCallService extends InCallService {
+    private static String LOG_TAG = "MockInCallService";
     private ArrayList<Call> mCalls = new ArrayList<>();
     private ArrayList<Call> mConferenceCalls = new ArrayList<>();
     private static InCallServiceCallbacks sCallbacks;
@@ -34,6 +39,7 @@
             new ArrayMap<Call, MockVideoCallCallback>();
 
     private static final Object sLock = new Object();
+    private static boolean mIsServiceUnbound;
 
     public static abstract class InCallServiceCallbacks {
         private MockInCallService mService;
@@ -152,9 +158,11 @@
 
     @Override
     public android.os.IBinder onBind(android.content.Intent intent) {
+        Log.i(LOG_TAG, "Service bounded");
         if (getCallbacks() != null) {
             getCallbacks().setService(this);
         }
+        mIsServiceUnbound = false;
         return super.onBind(intent);
     }
 
@@ -312,4 +320,16 @@
     public MockVideoCallCallback getVideoCallCallback(Call call) {
         return mVideoCallCallbacks.get(call);
     }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Log.i(LOG_TAG, "Service unbounded");
+        assertFalse(mIsServiceUnbound);
+        mIsServiceUnbound = true;
+        return super.onUnbind(intent);
+    }
+
+    public static boolean isServiceUnbound() {
+        return mIsServiceUnbound;
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
index 8ffcf48..e95a290 100644
--- a/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
@@ -62,7 +62,5 @@
             res.wait(CS_WAIT_MILLIS);
         }
         assertEquals(address, res[0]);
-
-        tearDownConnectionService(account.getAccountHandle());
     }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index f2422c9..bba9a44 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -18,6 +18,8 @@
 
 import static android.telecom.cts.TestUtils.shouldTestTelecom;
 
+import android.content.Context;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.TelecomManager;
@@ -35,15 +37,13 @@
         }
     }
 
-    // TODO: Need to send some commands to the UserManager via adb to do setup
+    /* TODO: Need to send some commands to the UserManager via adb to do setup
     public void testDisallowOutgoingCallsForSecondaryUser() {
+    } */
 
-    }
-
-    // TODO: Need to figure out a way to mock emergency calls without adb root
+    /* TODO: Need to figure out a way to mock emergency calls without adb root
     public void testOutgoingCallBroadcast_isSentForAllCalls() {
-
-    }
+    } */
 
     /**
      * Verifies that providing the EXTRA_START_CALL_WITH_SPEAKERPHONE extra starts the call with
@@ -68,11 +68,15 @@
             return;
         }
 
+        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        int expectedRoute = am.isWiredHeadsetOn() ?
+                CallAudioState.ROUTE_WIRED_HEADSET : CallAudioState.ROUTE_EARPIECE;
+
         final Bundle extras = new Bundle();
         extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
         placeAndVerifyCall(extras);
         verifyConnectionForOutgoingCall();
-        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+        assertAudioRoute(mInCallCallbacks.getService(), expectedRoute);
     }
 
     public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
@@ -80,8 +84,12 @@
             return;
         }
 
+        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        int expectedRoute = am.isWiredHeadsetOn() ?
+                CallAudioState.ROUTE_WIRED_HEADSET : CallAudioState.ROUTE_EARPIECE;
+
         placeAndVerifyCall();
         verifyConnectionForOutgoingCall();
-        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+        assertAudioRoute(mInCallCallbacks.getService(), expectedRoute);
     }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
index 3fc65ea..787966a 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
@@ -61,7 +61,6 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mContext = getInstrumentation().getContext();
         if (mShouldTestTelecom) {
             addRemoteConferenceCall();
             verifyRemoteConferenceObject(mRemoteConferenceObject, mRemoteConference, mConference);
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
index 79fb592..5b552a0 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
@@ -1210,7 +1210,6 @@
         };
         mRemoteConnectionObject.registerCallback(callback, handler);
         mRemoteConnection.createMockVideoProvider();
-        mRemoteConnection.setVideoProvider(mRemoteConnection.getMockVideoProvider());
         callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
         assertEquals(mRemoteConnectionObject, callbackInvoker.getArgs(0)[0]);
         mRemoteConnectionObject.unregisterCallback(callback);
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index 962df59..4936209 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -63,7 +63,8 @@
             return false;
         }
         final PackageManager pm = context.getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
+                pm.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
     }
 
     public static String setDefaultDialer(Instrumentation instrumentation, String packageName)
diff --git a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
index 466a90b..e7130ba 100644
--- a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
@@ -35,21 +35,12 @@
         }
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
-            mInCallCallbacks.getService().disconnectLastCall();
-            assertNumCalls(mInCallCallbacks.getService(), 0);
-        }
-        super.tearDown();
-    }
-
     public void testIncomingCallShortPress_acceptsCall() throws Exception {
         if (!mShouldTestTelecom) {
             return;
         }
 
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         final MockConnection connection = verifyConnectionForIncomingCall();
 
         final Call call = mInCallCallbacks.getService().getLastCall();
@@ -66,7 +57,7 @@
             return;
         }
 
-        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
         final MockConnection connection = verifyConnectionForIncomingCall();
 
         final Call call = mInCallCallbacks.getService().getLastCall();
@@ -119,16 +110,6 @@
         assertConnectionState(connection, Connection.STATE_DISCONNECTED);
     }
 
-    public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
-        if (!mShouldTestTelecom) {
-            return;
-        }
-
-        placeAndVerifyCall();
-        verifyConnectionForOutgoingCall();
-        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
-    }
-
     private void sendMediaButtonShortPress() throws Exception {
         sendMediaButtonPress(false /* longPress */);
     }
diff --git a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
index 176c50c..e15b45f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.SystemClock;
 import android.telephony.SmsManager;
@@ -84,6 +85,7 @@
     private Random mRandom;
     private SentReceiver mSentReceiver;
     private TelephonyManager mTelephonyManager;
+    private PackageManager mPackageManager;
 
     private static class SentReceiver extends BroadcastReceiver {
         private final Object mLock;
@@ -164,15 +166,26 @@
         mRandom = new Random();
         mTelephonyManager =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        mPackageManager = mContext.getPackageManager();
     }
 
     public void testSendMmsMessage() {
-        if (!mTelephonyManager.isSmsCapable()) {
-            Log.i(TAG, "testSendMmsMessage skipped: not SMS capable");
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            Log.i(TAG, "testSendMmsMessage skipped: no telephony available");
             return;
         }
 
         Log.i(TAG, "testSendMmsMessage");
+        // Prime the MmsService so that MMS config is loaded
+        final SmsManager smsManager = SmsManager.getDefault();
+        smsManager.getAutoPersisting();
+        // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+
         final Context context = getContext();
         // Register sent receiver
         mSentReceiver = new SentReceiver();
@@ -193,7 +206,7 @@
         // Send
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 context, 0, new Intent(ACTION_MMS_SENT), 0);
-        SmsManager.getDefault().sendMultimediaMessage(context,
+        smsManager.sendMultimediaMessage(context,
                 contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
         assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
         sendFile.delete();
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 41fe996..8b94d00 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -225,7 +225,10 @@
                 break;
 
             case TelephonyManager.PHONE_TYPE_NONE:
-                if (mCm.getNetworkInfo(ConnectivityManager.TYPE_WIFI) != null) {
+                boolean nwSupported = mCm.isNetworkSupported(mCm.TYPE_WIFI);
+                PackageManager packageManager = getContext().getPackageManager();
+                // only check serial number & MAC address if device report wifi feature
+                if (packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
                     assertSerialNumber();
                     assertMacAddress(getWifiMacAddress());
                 } else if (mCm.getNetworkInfo(ConnectivityManager.TYPE_BLUETOOTH) != null) {
diff --git a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
index 6eb09eb..9ab815f 100644
--- a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
@@ -94,20 +94,20 @@
         final long ONE_SECOND_IN_MS = 1000;
         assertEquals("0 minutes ago",
                 DateUtils.getRelativeTimeSpanString(mBaseTime - ONE_SECOND_IN_MS));
-        assertEquals("in 0 minutes",
+        assertEquals("In 0 minutes",
                 DateUtils.getRelativeTimeSpanString(mBaseTime + ONE_SECOND_IN_MS));
 
         final long ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS;
         assertEquals("1 minute ago", DateUtils.getRelativeTimeSpanString(0, ONE_MINUTE_IN_MS,
                 DateUtils.MINUTE_IN_MILLIS));
-        assertEquals("in 1 minute", DateUtils.getRelativeTimeSpanString(ONE_MINUTE_IN_MS, 0,
+        assertEquals("In 1 minute", DateUtils.getRelativeTimeSpanString(ONE_MINUTE_IN_MS, 0,
                 DateUtils.MINUTE_IN_MILLIS));
 
         final long ONE_HOUR_IN_MS = 60 * 60 * 1000;
         final long TWO_HOURS_IN_MS = 2 * ONE_HOUR_IN_MS;
         assertEquals("2 hours ago", DateUtils.getRelativeTimeSpanString(mBaseTime - TWO_HOURS_IN_MS,
                 mBaseTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_NUMERIC_DATE));
-        assertEquals("in 2 hours", DateUtils.getRelativeTimeSpanString(mBaseTime + TWO_HOURS_IN_MS,
+        assertEquals("In 2 hours", DateUtils.getRelativeTimeSpanString(mBaseTime + TWO_HOURS_IN_MS,
                 mBaseTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_NUMERIC_DATE));
     }
 
diff --git a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
index 10d08d0..482edb0 100644
--- a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
@@ -255,7 +255,7 @@
 
         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
                 KeyEvent.KEYCODE_DPAD_UP, noMetaEvent));
-        // |first line
+        // first lin|e
         // second line
         // last line
         assertSelection(0);
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 3c6028f..6514402 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -165,6 +165,9 @@
             // Wait for things to settle.
             getUiDevice().waitForIdle();
 
+            // Wait for Activity draw finish
+            getInstrumentation().waitForIdleSync();
+
             // Clear the window animation stats to be with a clean slate.
             uiAutomation.clearWindowAnimationFrameStats();
 
@@ -177,6 +180,9 @@
             // Wait for things to settle.
             getUiDevice().waitForIdle();
 
+            // Wait for Activity draw finish
+            getInstrumentation().waitForIdleSync();
+
             // Get the frame stats.
             WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
index cb62694..c97e020 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
@@ -28,13 +28,19 @@
  */
 public class SamplePointVerifier extends BitmapVerifier {
     private static final String TAG = "SamplePoint";
-    private Point[] mTestPoints;
-    private int[] mExpectedColors;
-    private int mTolerance = 20;
+    private static final int DEFAULT_TOLERANCE = 20;
+    private final Point[] mTestPoints;
+    private final int[] mExpectedColors;
+    private final int mTolerance;
 
     public SamplePointVerifier(Point[] testPoints, int[] expectedColors) {
+        this(testPoints, expectedColors, DEFAULT_TOLERANCE);
+    }
+
+    public SamplePointVerifier(Point[] testPoints, int[] expectedColors, int tolerance) {
         mTestPoints = testPoints;
         mExpectedColors = expectedColors;
+        mTolerance = tolerance;
     }
 
     @Override
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 2726dac..38777a2 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -16,6 +16,7 @@
 
 package android.uirendering.cts.testclasses;
 
+import android.content.pm.PackageManager;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -163,6 +164,9 @@
 
     @SmallTest
     public void testWebViewClipWithCircle() {
+        if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+            return; // no WebView to run test on
+        }
         createTest()
                 // golden client - draw a simple non-AA circle
                 .addCanvasClient(new CanvasClient() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
index 2061023..4582935 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
@@ -32,6 +32,7 @@
         if (getActivity().getOnTv()) {
             shadowColorValue = 0xBB;
         }
+        // Use a higher threshold (30) than default value (20);
         SamplePointVerifier verifier = new SamplePointVerifier(
                 new Point[] {
                         // view area
@@ -46,7 +47,8 @@
                         Color.WHITE,
                         Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
                         Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
-                });
+                },
+                30);
         createTest()
                 .addLayout(R.layout.simple_shadow_layout, null, true/* HW only */)
                 .runWithVerifier(verifier);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index d585f5f..57c67bd 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -48,7 +48,7 @@
                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
         mHandler = new RenderSpecHandler();
         int uiMode = getResources().getConfiguration().uiMode;
-        mOnTv = (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) == Configuration.UI_MODE_TYPE_TELEVISION;
+        mOnTv = (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
     }
 
     public boolean getOnTv() {
diff --git a/tests/tests/view/src/android/view/cts/MotionEventTest.java b/tests/tests/view/src/android/view/cts/MotionEventTest.java
index cdedca4..10ea33a 100644
--- a/tests/tests/view/src/android/view/cts/MotionEventTest.java
+++ b/tests/tests/view/src/android/view/cts/MotionEventTest.java
@@ -180,6 +180,40 @@
         assertEquals(mMotionEvent2.getDeviceId(), motionEvent.getDeviceId());
     }
 
+    public void testReadFromParcelWithInvalidPointerCountSize() {
+        Parcel parcel = Parcel.obtain();
+        mMotionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+        // Move to pointer id count.
+        parcel.setDataPosition(4);
+        parcel.writeInt(17);
+
+        parcel.setDataPosition(0);
+        try {
+            MotionEvent.CREATOR.createFromParcel(parcel);
+            fail("deserialized invalid parcel");
+        } catch (RuntimeException e) {
+            // Expected.
+        }
+    }
+
+    public void testReadFromParcelWithInvalidSampleSize() {
+        Parcel parcel = Parcel.obtain();
+        mMotionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+        // Move to sample count.
+        parcel.setDataPosition(2 * 4);
+        parcel.writeInt(0x000f0000);
+
+        parcel.setDataPosition(0);
+        try {
+            MotionEvent.CREATOR.createFromParcel(parcel);
+            fail("deserialized invalid parcel");
+        } catch (RuntimeException e) {
+            // Expected.
+        }
+    }
+
     public void testToString() {
         // make sure this method never throw exception.
         mMotionEvent2.toString();
diff --git a/tests/tests/voiceinteraction/AndroidTest.xml b/tests/tests/voiceinteraction/AndroidTest.xml
index 374c216..fa1ab70 100644
--- a/tests/tests/voiceinteraction/AndroidTest.xml
+++ b/tests/tests/voiceinteraction/AndroidTest.xml
@@ -19,7 +19,5 @@
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionApp.apk" />
     <option name="run-command:run-command"
          value="settings put secure voice_interaction_service android.voiceinteraction.service/.MainInteractionService" />
-    <option name="run-command:teardown-command"
-         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionTestCases.apk" />
 </configuration>
diff --git a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
index 1c9ee71..15069ec 100644
--- a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
@@ -23,7 +23,7 @@
 
       <activity android:name="TestApp"
                 android:label="Voice Interaction Test App"
-                android:theme="@android:style/Theme.Material.Light">
+                android:theme="@android:style/Theme.DeviceDefault">
           <intent-filter>
               <action android:name="android.intent.action.TEST_APP" />
               <category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
index 0a3974d..e3be691 100644
--- a/tests/tests/voicesettings/AndroidTest.xml
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -17,7 +17,5 @@
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsService.apk" />
     <option name="run-command:run-command"
          value="settings put secure voice_interaction_service android.voicesettings.service/.MainInteractionService" />
-    <option name="run-command:teardown-command"
-         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
     <option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsTestCases.apk" />
 </configuration>
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
index c2b7e18..aff1160 100644
--- a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
@@ -24,6 +24,7 @@
 import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
 import static android.provider.Settings.EXTRA_BATTERY_SAVER_MODE_ENABLED;
 
+import android.app.VoiceInteractor;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncTask;
@@ -138,9 +139,10 @@
 
     @Override
     public void onRequestCompleteVoice(CompleteVoiceRequest request) {
-        CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
+        VoiceInteractor.Prompt prompt = request.getVoicePrompt();
+        CharSequence message = (prompt != null ? prompt.getVoicePromptAt(0) : "(none)");
         Log.i(TAG, "in Session testcasetype = " + mTestType +
-                ", onRequestCompleteVoice recvd. message = " + prompt);
+                ", onRequestCompleteVoice recvd. message = " + message);
         AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setCompleteReq(true);
         newTask().execute(asyncTaskArg);
     }
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
index 8abe396..9ce743e 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -16,6 +16,8 @@
 
 package android.voicesettings.cts;
 
+import static android.provider.Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE;
+
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.util.Log;
@@ -33,9 +35,20 @@
     }
 
     public void testAll() throws Exception {
+        if (!isIntentSupported(ACTION_VOICE_CONTROL_AIRPLANE_MODE)) {
+            Log.e(TAG, "Voice intent for Airplane Mode NOT supported. existing the test");
+            return;
+        }
+        int mode;
+        try {
+            mode = getMode();
+            Log.i(TAG, "Before testing, AIRPLANE_MODE is set to: " + mode);
+        } catch (Settings.SettingNotFoundException e) {
+            // if the mode is not supported, don't run the test.
+            Log.i(TAG, "airplane mode is not found in Settings. Skipping AirplaneModeTest");
+            return;
+        }
         startTestActivity("AIRPLANE_MODE");
-        int mode = getMode();
-        Log.i(TAG, "Before testing, AIRPLANE_MODE is set to: " + mode);
         if (mode == AIRPLANE_MODE_IS_OFF) {
             // mode is currently OFF.
             // run a test to turn it on.
@@ -70,7 +83,7 @@
         return true;
     }
 
-    private int getMode() throws Exception {
+    private int getMode() throws Settings.SettingNotFoundException {
         return Settings.Global.getInt(mContext.getContentResolver(),
             Settings.Global.AIRPLANE_MODE_ON);
     }
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
index 3d1357a..983e27b 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -16,6 +16,8 @@
 
 package android.voicesettings.cts;
 
+import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
+
 import android.content.Context;
 import android.os.PowerManager;
 import android.util.Log;
@@ -30,6 +32,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!isIntentSupported(ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE)) {
+            Log.e(TAG, "Voice intent for Battery Saver Mode NOT supported. existing the test");
+            return;
+        }
         startTestActivity("BATTERYSAVER_MODE");
         boolean modeIsOn = isModeOn();
         Log.i(TAG, "Before testing, BATTERYSAVER_MODE is set to: " + modeIsOn);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
index c28feab..529c160 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
@@ -66,6 +67,17 @@
         super.tearDown();
     }
 
+    protected boolean isIntentSupported(String intentStr) {
+        Intent intent = new Intent(intentStr);
+        final PackageManager manager = mContext.getPackageManager();
+        assertNotNull(manager);
+        if (manager.resolveActivity(intent, 0) == null) {
+            Log.i(TAG, "No Voice Activity found for the intent: " + intentStr);
+            return false;
+        }
+        return true;
+    }
+
     protected void startTestActivity(String intentSuffix) {
         Intent intent = new Intent();
         intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index 8c2efbe..420da8f 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -16,6 +16,7 @@
 
 package android.voicesettings.cts;
 
+import static android.provider.Settings.ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE;
 import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
 import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
 
@@ -39,9 +40,20 @@
     }
 
     public void testAll() throws Exception {
+        if (!isIntentSupported(ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE)) {
+            Log.e(TAG, "Voice intent for Zen Mode NOT supported. existing the test");
+            return;
+        }
+        int mode;
+        try {
+            mode = getMode();
+            Log.i(TAG, "Before testing, zen-mode is set to: " + mode);
+        } catch (Settings.SettingNotFoundException e) {
+            // if the mode is not supported, don't run the test.
+            Log.i(TAG, "zen_mode is not found in Settings. Skipping ZenModeTest");
+            return;
+        }
         startTestActivity("ZEN_MODE");
-        int mode = getMode();
-        Log.i(TAG, "Before testing, zen-mode is set to: " + mode);
         if (mode == ZEN_MODE_IS_OFF) {
             // mode is currently OFF.
             // run a test to turn it on.
@@ -85,7 +97,7 @@
         return true;
     }
 
-    private int getMode() throws Exception {
+    private int getMode() throws Settings.SettingNotFoundException {
         return Settings.Global.getInt(mContext.getContentResolver(), ZEN_MODE);
     }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 6a2240e..ccbdd56 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -317,13 +317,8 @@
             //expected
         }
 
-        try {
-            assertEquals(AdapterView.INVALID_POSITION,
-                    mAdapterView.getPositionForView(new ImageView(mActivity)));
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            //expected
-        }
+        assertEquals(AdapterView.INVALID_POSITION,
+                mAdapterView.getPositionForView(new ImageView(mActivity)));
     }
 
     public void testChangeFocusable() {
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index fa930df..782e6ab 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -31,7 +31,7 @@
     @Option(name="cts-install-path", description="the path to the cts installation to use")
     private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
 
-    public static final String CTS_BUILD_VERSION = "6.0_r0";
+    public static final String CTS_BUILD_VERSION = "6.0_r1";
     public static final String CTS_PACKAGE = "com.android.cts.tradefed.testtype";
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
index fe096bd..61561a5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
@@ -17,6 +17,7 @@
 
 import com.android.cts.util.AbiUtils;
 import com.android.cts.tradefed.result.CtsXmlResultReporter;
+import com.android.ddmlib.IDevice;
 import com.android.ddmlib.Log;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IFolderBuildInfo;
@@ -48,7 +49,7 @@
             "com.android.compatibility.common.deviceinfo";
     private static final String EXTENDED_INSTRUMENTATION_NAME =
             "com.android.compatibility.common.deviceinfo.DeviceInfoInstrument";
-    private static final String DEVICE_RESULT_DIR = "/sdcard/device-info-files";
+    private static final String DEVICE_INFO_FILES = "device-info-files";
 
     public static final Set<String> IDS = new HashSet<String>();
     public static final Set<String> EXTENDED_IDS = new HashSet<String>();
@@ -86,7 +87,8 @@
             ITestInvocationListener listener, IBuildInfo buildInfo)
             throws DeviceNotAvailableException {
         // Clear files in device test result directory
-        device.executeShellCommand(String.format("rm -rf %s", DEVICE_RESULT_DIR));
+        String deviceResultDir = getDeviceResultDir(device);
+        device.executeShellCommand(String.format("rm -rf %s", deviceResultDir));
         runInstrumentation(device, abi, testApkDir, listener, EXTENDED_APK_NAME,
             EXTENDED_APP_PACKAGE_NAME, EXTENDED_INSTRUMENTATION_NAME);
         // Copy files in remote result directory to local directory
@@ -126,9 +128,16 @@
             Log.e(LOG_TAG, "Local result directory is null or is not a directory");
             return;
         }
+
+        localResultDir = new File(localResultDir, DEVICE_INFO_FILES);
+        localResultDir.mkdirs();
+
+
+        String deviceResultDir = getDeviceResultDir(device);
+
         // Pull files from device result directory to local result directory
         String command = String.format("adb -s %s pull %s %s", device.getSerialNumber(),
-                DEVICE_RESULT_DIR, localResultDir.getAbsolutePath());
+                deviceResultDir, localResultDir.getAbsolutePath());
         if (!execute(command)) {
             Log.e(LOG_TAG, String.format("Failed to run %s", command));
         }
@@ -143,4 +152,14 @@
             return false;
         }
     }
+
+    private static String getDeviceResultDir(ITestDevice device) {
+        String externalStorePath = device.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE);
+        if (externalStorePath == null) {
+            Log.e(LOG_TAG, String.format(
+                    "Failed to get external storage path on device %s", device.getSerialNumber()));
+            return null;
+        }
+        return String.format("%s/%s", externalStorePath, DEVICE_INFO_FILES);
+    }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index f5a3d02..45224f6 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -254,12 +254,12 @@
             if (perfResult == null) {
                 perfResult = CtsHostStore.removeCtsResult(mDeviceSerial, mAbi, test.toString());
             }
-            if (perfResult != null) {
+            Test result = findTest(test);
+            if (perfResult != null && !result.getResult().equals(CtsTestStatus.FAIL)) {
                 // CTS result is passed in Summary++++Details format.
                 // Extract Summary and Details, and pass them.
                 Matcher m = mCtsLogPattern.matcher(perfResult);
                 if (m.find()) {
-                    Test result = findTest(test);
                     result.setResultStatus(CtsTestStatus.PASS);
                     result.setSummary(m.group(1));
                     result.setDetails(m.group(2));
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
index 43aaf98..6f4d42d 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
@@ -63,7 +63,7 @@
     private static final BatchRunConfiguration DEFAULT_CONFIG =
         new BatchRunConfiguration("rgba8888d24s8", "unspecified", "window");
 
-    private static final int UNRESPOSIVE_CMD_TIMEOUT_MS = 60000; // one minute
+    private static final int UNRESPOSIVE_CMD_TIMEOUT_MS = 10*60*1000; // ten minutes
 
     private final String mPackageName;
     private final String mName;
@@ -427,6 +427,7 @@
                 if (!mGotTestResult) {
                     result.allInstancesPassed = false;
                     result.errorMessages.put(mRunConfig, INCOMPLETE_LOG_MESSAGE);
+                    CLog.i("Test %s failed as it ended before receiving result.", mCurrentTestId);
                 }
 
                 if (mLogData && mCurrentTestLog != null && mCurrentTestLog.length() > 0) {
@@ -477,6 +478,7 @@
                 mPendingResults.get(mCurrentTestId)
                         .errorMessages.put(mRunConfig, codeError + ": " + details);
                 mGotTestResult = true;
+                CLog.e("Got invalid result code '%s' for test %s", code, mCurrentTestId);
             }
         }
 
@@ -950,6 +952,7 @@
         private void killDeqpProcess() throws DeviceNotAvailableException,
                 ProcessKillFailureException {
             for (Integer processId : getDeqpProcessPids()) {
+                CLog.i("Killing deqp device process with ID %d", processId);
                 mDevice.executeShellCommand(String.format("kill -9 %d", processId));
             }
 
@@ -958,6 +961,7 @@
             // check that processes actually died
             if (getDeqpProcessPids().iterator().hasNext()) {
                 // a process is still alive, killing failed
+                CLog.w("Failed to kill all deqp processes on device");
                 throw new ProcessKillFailureException();
             }
         }
@@ -1336,15 +1340,19 @@
                     UNRESPOSIVE_CMD_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         } catch (TimeoutException ex) {
             // Opening connection timed out
+            CLog.e("Opening connection timed out for command: '%s'", command);
             throw new AdbComLinkOpenError("opening connection timed out", ex);
         } catch (AdbCommandRejectedException ex) {
             // Command rejected
+            CLog.e("Device rejected command: '%s'", command);
             throw new AdbComLinkOpenError("command rejected", ex);
         } catch (IOException ex) {
             // shell command channel killed
+            CLog.e("Channel died for command: '%s'", command);
             throw new AdbComLinkKilledError("command link killed", ex);
         } catch (ShellCommandUnresponsiveException ex) {
             // shell command halted
+            CLog.e("No output from command in %d ms: '%s'", UNRESPOSIVE_CMD_TIMEOUT_MS, command);
             throw new AdbComLinkKilledError("command link hung", ex);
         }
     }
@@ -1460,11 +1468,14 @@
         // interrupted, try to recover
         if (interruptingError != null) {
             if (interruptingError instanceof AdbComLinkOpenError) {
+                CLog.i("Recovering from comm link error");
                 mDeviceRecovery.recoverConnectionRefused();
             } else if (interruptingError instanceof AdbComLinkKilledError) {
+                CLog.i("Recovering from comm link killed");
                 mDeviceRecovery.recoverComLinkKilled();
             } else if (interruptingError instanceof RunInterruptedException) {
                 // external run interruption request. Terminate immediately.
+                CLog.i("Run termination requested. Throwing forward.");
                 throw (RunInterruptedException)interruptingError;
             } else {
                 CLog.e(interruptingError);
@@ -1473,6 +1484,7 @@
 
             // recoverXXX did not throw => recovery succeeded
         } else if (!parser.wasSuccessful()) {
+            CLog.i("Parse not successful. Will attempt comm link recovery.");
             mDeviceRecovery.recoverComLinkKilled();
             // recoverXXX did not throw => recovery succeeded
         }
@@ -1495,8 +1507,10 @@
                 // This is required so that a consistently crashing or non-existent tests will
                 // not cause futile (non-terminating) re-execution attempts.
                 if (mInstanceListerner.getCurrentTestId() != null) {
+                    CLog.w("Test '%s' started, but not completed", onlyTest);
                     mInstanceListerner.abortTest(onlyTest, INCOMPLETE_LOG_MESSAGE);
                 } else {
+                    CLog.w("Test '%s' could not start", onlyTest);
                     mInstanceListerner.abortTest(onlyTest, NOT_EXECUTABLE_LOG_MESSAGE);
                 }
             } else if (wasTestExecuted) {
@@ -1827,6 +1841,7 @@
                 uninstallTestApk();
             } else {
                 // Pass all tests if OpenGL ES version is not supported
+                CLog.i("Package %s not supported by the device. Tests trivially pass.", mPackageName);
                 fakePassTests(listener);
             }
         } catch (CapabilityQueryFailureException ex) {
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 2b7e093..2eb5145 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -191,8 +191,6 @@
     for package, test_list in small_tests.iteritems():
       plan.Include(package+'$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    for package, test_list in temporarily_known_failure_tests.iteritems():
-      plan.ExcludeTests(package, test_list)
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     for package, test_list in releasekey_tests.iteritems():
@@ -273,9 +271,6 @@
       plan.Exclude(package+'$')
     for package, tests_list in new_test_packages.iteritems():
       plan.Exclude(package+'$')
-    for package, test_list in temporarily_known_failure_tests.iteritems():
-      plan.Include(package+'$')
-      plan.IncludeTests(package, test_list)
     plan.Exclude(r'com\.drawelements\.')
     plan.Exclude(r'android\.hardware$')
     plan.Exclude(r'android\.media$')
@@ -286,6 +281,15 @@
       plan.ExcludeTests(package, test_list)
     for package, test_list in releasekey_tests.iteritems():
       plan.ExcludeTests(package, test_list)
+    self.__WritePlan(plan, 'CTS-m-tests')
+
+
+    # CTS - sub plan for new test packages added for staging
+    plan = tools.TestPlan(packages)
+    plan.Exclude('.*')
+    for package, test_list in temporarily_known_failure_tests.iteritems():
+      plan.Include(package+'$')
+      plan.IncludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-staging')
 
     plan = tools.TestPlan(packages)
@@ -461,11 +465,47 @@
   """ Construct a defaultdict that maps package name to a list of tests
       that are known failures during dev cycle but expected to be fixed before launch """
   return {
-      'android.bluetooth' : [
-          'android.bluetooth.cts.BluetoothLeScanTest#testBasicBleScan',
-          'android.bluetooth.cts.BluetoothLeScanTest#testBatchScan',
-          'android.bluetooth.cts.BluetoothLeScanTest#testOpportunisticScan',
-          'android.bluetooth.cts.BluetoothLeScanTest#testScanFilter',],
+      'android.alarmclock' : [
+          'android.alarmclock.cts.DismissAlarmTest#testAll',
+          'android.alarmclock.cts.SetAlarmTest#testAll',
+          'android.alarmclock.cts.SnoozeAlarmTest#testAll',
+      ],
+      'android.assist' : [
+          'android.assist.cts.AssistantContentViewTest',
+          'android.assist.cts.ExtraAssistDataTest',
+          'android.assist.cts.FocusChangeTest',
+          'android.assist.cts.LargeViewHierarchyTest',
+          'android.assist.cts.ScreenshotTest',
+          'android.assist.cts.TextViewTest',
+          'android.assist.cts.WebViewTest',
+      ],
+      'android.calllog' : [
+          'android.calllog.cts.CallLogBackupTest#testSingleCallBackup',
+      ],
+      'android.dumpsys' : [
+          'android.dumpsys.cts.DumpsysHostTest#testBatterystatsOutput',
+          'android.dumpsys.cts.DumpsysHostTest#testGfxinfoFramestats',
+      ],
+      'android.telecom' : [
+          'android.telecom.cts.ExtendedInCallServiceTest#testAddNewOutgoingCallAndThenDisconnect',
+          'android.telecom.cts.RemoteConferenceTest#testRemoteConferenceCallbacks_ConferenceableConnections',
+      ],
+      'android.transition' : [
+          'android.transition.cts.ChangeScrollTest#testChangeScroll',
+      ],
+      'android.voicesettings' : [
+          'android.voicesettings.cts.ZenModeTest#testAll',
+      ],
+      'com.android.cts.systemui' : [
+          'com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons',
+      ],
+      'com.android.cts.app.os' : [
+          'com.android.cts.app.os.OsHostTests#testNonExportedActivities',
+      ],
+      'com.android.cts.devicepolicy' : [
+          'com.android.cts.devicepolicy.MixedDeviceOwnerTest#testPackageInstallUserRestrictions',
+          'com.android.cts.devicepolicy.MixedProfileOwnerTest#testPackageInstallUserRestrictions',
+      ],
       '' : []}
 
 def LogGenerateDescription(name):