Merge "CamreaITS: several ITS tests tweak" into lmp-mr1-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 16f9ec1..e8e7485 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -34,6 +34,7 @@
     CtsSplitApp_arm64-v8a \
     CtsSplitApp_mips64 \
     CtsSplitApp_mips \
+    CtsSplitAppDiffRevision \
     CtsSplitAppDiffVersion \
     CtsSplitAppDiffCert \
     CtsSplitAppFeature \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index f195cf5..3b2eb46 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.cts.verifier"
       android:versionCode="5"
-      android:versionName="5.0_r2.5">
+      android:versionName="5.1_r0.5">
 
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="21"/>
 
@@ -500,6 +500,10 @@
                 android:label="@string/nfc_hce_payment_dynamic_aids_emulator"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
 
+        <activity android:name=".nfc.hce.LargeNumAidsEmulatorActivity"
+                  android:label="@string/nfc_hce_large_num_aids_emulator"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
         <activity android:name=".nfc.hce.PrefixPaymentEmulatorActivity"
                 android:label="@string/nfc_hce_payment_prefix_aids_emulator"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
@@ -635,6 +639,15 @@
             </intent-filter>
             <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/access_prefix_aid_list"/>
         </service>
+        <service android:name=".nfc.hce.LargeNumAidsService" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_1"/>
+        </service>
 
         <!-- Service used for Camera ITS tests -->
         <service android:name=".camera.its.ItsService" >
@@ -1012,6 +1025,10 @@
             <meta-data android:name="test_category" android:value="@string/test_category_notifications" />
             <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" />
+            <meta-data android:name="test_excluded_features"
+                    android:value="android.software.leanback" />
         </activity>
 
         <service android:name=".notifications.MockListener"
@@ -1262,8 +1279,7 @@
         </activity>
 
 
-        <!-- TODO: enable when the test can be executed without leaving marks -->
-        <!-- activity android:name=".managedprovisioning.ByodFlowTestActivity"
+        <activity android:name=".managedprovisioning.ByodFlowTestActivity"
                 android:launchMode="singleTask"
                 android:label="@string/provisioning_byod">
             <intent-filter>
@@ -1276,12 +1292,13 @@
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_managed_provisioning" />
             <meta-data android:name="test_required_features" android:value="android.software.managed_users:android.software.device_admin" />
-        </activity-->
+        </activity>
 
         <activity android:name=".managedprovisioning.ByodHelperActivity">
             <intent-filter>
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_QUERY" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_REMOVE" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK" />
                 <category android:name="android.intent.category.DEFAULT"></category>
             </intent-filter>
         </activity>
@@ -1300,6 +1317,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".managedprovisioning.WorkNotificationTestActivity">
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.WORK_NOTIFICATION" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.CLEAR_WORK_NOTIFICATION" />
+                <category android:name="android.intent.category.DEFAULT"></category>
+            </intent-filter>
+        </activity>
+
         <receiver android:name=".managedprovisioning.DeviceAdminTestReceiver"
                 android:label="@string/provisioning_byod_device_admin"
                 android:permission="android.permission.BIND_DEVICE_ADMIN">
@@ -1386,6 +1411,15 @@
                     android:value="android.software.live_tv" />
         </activity>
 
+        <activity android:name=".screenpinning.ScreenPinningTestActivity"
+            android:label="@string/screen_pinning_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_other" />
+        </activity>
+
         <activity android:name=".tv.MockTvInputSettingsActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/apps/CtsVerifier/assets/scripts/execute_power_tests.py b/apps/CtsVerifier/assets/scripts/execute_power_tests.py
index d1c2dac..d07cb36 100755
--- a/apps/CtsVerifier/assets/scripts/execute_power_tests.py
+++ b/apps/CtsVerifier/assets/scripts/execute_power_tests.py
@@ -25,57 +25,108 @@
 import pkgutil
 import threading
 import Queue
+import traceback
+import math
+import bisect
+from bisect import bisect_left
 
-# queue to signal thread to exit
-signal_exit_q = Queue.Queue()
-signal_abort = Queue.Queue()
+"""
+scipy, numpy and matplotlib are python packages that can be installed
+from: http://www.scipy.org/
+
+"""
+import scipy
+import matplotlib.pyplot as plt
 
 # let this script know about the power monitor implementations
 sys.path = [os.path.basename(__file__)] + sys.path
-available_monitors = [name for _, name, _ in pkgutil.iter_modules(
-    [os.path.join(os.path.dirname(__file__),'power_monitors')]) if not name.startswith('_')]
+available_monitors = [
+    name
+    for _, name, _ in pkgutil.iter_modules(
+        [os.path.join(os.path.dirname(__file__), "power_monitors")])
+    if not name.startswith("_")]
 
-APK = os.path.join( os.path.dirname(__file__), '..', "CtsVerifier.apk")
+APK = os.path.join(os.path.dirname(__file__), "..", "CtsVerifier.apk")
 
 FLAGS = flags.FLAGS
 
-# whether to use a strict delay to ensure screen is off, or attempt to use power measurements
-USE_STRICT_DELAY = False
-if USE_STRICT_DELAY:
-    DELAY_SCREEN_OFF = 30.0
-else:
-    DELAY_SCREEN_OFF = 2.0
+# DELAY_SCREEN_OFF is the number of seconds to wait for baseline state
+DELAY_SCREEN_OFF = 20.0
 
 # whether to log data collected to a file for each sensor run:
 LOG_DATA_TO_FILE = True
 
 logging.getLogger().setLevel(logging.ERROR)
 
+
 def do_import(name):
     """import a module by name dynamically"""
     mod = __import__(name)
-    components = name.split('.')
+    components = name.split(".")
     for comp in components[1:]:
         mod = getattr(mod, comp)
     return mod
 
+class PowerTestException(Exception):
+    """
+    Definition of specialized Exception class for CTS power tests
+    """
+    def __init__(self, message):
+        self._error_message = message
+    def __str__(self):
+        return self._error_message
 
 class PowerTest:
-    """Class to run a suite of power tests"""
+    """Class to run a suite of power tests. This has methods for obtaining
+    measurements from the power monitor (through the driver) and then
+    processing it to determine baseline and AP suspend state and
+    measure ampere draw of various sensors.
+    Ctrl+C causes a keyboard interrupt exception which terminates the test."""
 
     # Thresholds for max allowed power usage per sensor tested
-    MAX_ACCEL_POWER = 0.08  # Amps
-    MAX_MAG_POWER = 0.08  # Amps
-    MAX_GYRO_POWER = 0.08  # Amps
-    MAX_SIGMO_POWER = 0.08 # Amps
-    MAX_STEP_COUNTER_POWER = 0.08 # Amps
-    MAX_STEP_DETECTOR_POWER = 0.08 # Amps
+    # TODO: Accel, Mag and Gyro have no maximum power specified in the CDD;
+    # the following numbers are bogus and will be replaced soon by what
+    # the device reports (from Sensor.getPower())
+    MAX_ACCEL_AMPS = 0.08  # Amps
+    MAX_MAG_AMPS = 0.08  # Amps
+    MAX_GYRO_AMPS = 0.08  # Amps
+    MAX_SIGMO_AMPS = 0.08  # Amps
 
-
-    PORT = 0  # any available port
+    # TODO: The following numbers for step counter, etc must be replaced by
+    # the numbers specified in CDD for low-power sensors. The expected current
+    # draw must be computed from the specified power and the voltage used to
+    # power the device (specified from a config file).
+    MAX_STEP_COUNTER_AMPS = 0.08  # Amps
+    MAX_STEP_DETECTOR_AMPS = 0.08  # Amps
+    # The variable EXPECTED_AMPS_VARIATION_HALF_RANGE denotes the expected
+    # variation of  the ampere measurements
+    # around the mean value at baseline state. i.e. we expect most of the
+    # ampere measurements at baseline state to vary around the mean by
+    # between +/- of the number below
+    EXPECTED_AMPS_VARIATION_HALF_RANGE = 0.0005
+    # The variable THRESHOLD_BASELINE_SAMPLES_FRACTION denotes the minimum fraction of samples that must
+    # be in the range of variation defined by EXPECTED_AMPS_VARIATION_HALF_RANGE
+    # around the mean baseline for us to decide that the phone has settled into
+    # its baseline state
+    THRESHOLD_BASELINE_SAMPLES_FRACTION = 0.86
+    # The variable MAX_PERCENTILE_AP_SCREEN_OFF_AMPS denotes the maximum ampere
+    # draw that the device can consume when it has gone to suspend state with
+    # one or more sensors registered and batching samples (screen and AP are
+    # off in this case)
+    MAX_PERCENTILE_AP_SCREEN_OFF_AMPS = 0.030  # Amps
+    # The variable PERCENTILE_MAX_AP_SCREEN_OFF denotes the fraction of ampere
+    # measurements that must be below the specified maximum amperes
+    # MAX_PERCENTILE_AP_SCREEN_OFF_AMPS for us to decide that the phone has
+    # reached suspend state.
+    PERCENTILE_MAX_AP_SCREEN_OFF = 0.95
     DOMAIN_NAME = "/android/cts/powertest"
+    # SAMPLE_COUNT_NOMINAL denotes the typical number of measurements of amperes
+    # to collect from the power monitor
     SAMPLE_COUNT_NOMINAL = 1000
+    # RATE_NOMINAL denotes the nominal frequency at which ampere measurements
+    # are taken from the monsoon power monitor
     RATE_NOMINAL = 100
+    ENABLE_PLOTTING = False
 
     REQUEST_EXTERNAL_STORAGE = "EXTERNAL STORAGE?"
     REQUEST_EXIT = "EXIT"
@@ -87,103 +138,157 @@
     REQUEST_SCREEN_OFF = "SCREEN OFF"
     REQUEST_SHOW_MESSAGE = "MESSAGE %s"
 
+    NEGATIVE_AMPERE_ERROR_MESSAGE = (
+        "Negative ampere draw measured, possibly due to power "
+        "supply from USB cable. Check the setup of device and power "
+        "monitor to make sure that the device is not connected "
+        "to machine via USB directly. The device should be "
+        "connected to the USB slot in the power monitor. It is okay "
+        "to change the wiring when the test is in progress.")
 
-    def __init__(self):
+
+    def __init__(self, max_baseline_amps):
+        """
+        Args:
+            max_baseline_amps: The maximum value of baseline amperes
+                    that we expect the device to consume at baseline state.
+                    This can be different between models of phones.
+        """
         power_monitors = do_import("power_monitors.%s" % FLAGS.power_monitor)
         testid = time.strftime("%d_%m_%Y__%H__%M_%S")
         self._power_monitor = power_monitors.Power_Monitor(log_file_id = testid)
+        self._tcp_connect_port = 0  # any available port
         print ("Establishing connection to device...")
         self.setUsbEnabled(True)
         status = self._power_monitor.GetStatus()
         self._native_hz = status["sampleRate"] * 1000
+        # the following describes power test being run (i.e on what sensor
+        # and what type of test. This is used for logging.
         self._current_test = "None"
-        self._external_storage = self.executeOnDevice(PowerTest.REQUEST_EXTERNAL_STORAGE,
-                                                      reportErrors=True)
+        self._external_storage = self.executeOnDevice(PowerTest.REQUEST_EXTERNAL_STORAGE)
+        self._max_baseline_amps = max_baseline_amps
 
     def __del__(self):
         self.finalize()
 
     def finalize(self):
         """To be called upon termination of host connection to device"""
-        if PowerTest.PORT > 0:
-            # tell device side to exit connection loop, and remove the forwarding connection
-            self.executeOnDevice(PowerTest.REQUEST_EXIT, reportErrors=False)
-            self.executeLocal("adb forward --remove tcp:%d" % PowerTest.PORT)
-        PowerTest.PORT = 0
+        if self._tcp_connect_port > 0:
+            # tell device side to exit connection loop, and remove the forwarding
+            # connection
+            self.executeOnDevice(PowerTest.REQUEST_EXIT, reportErrors = False)
+            self.executeLocal("adb forward --remove tcp:%d" % self._tcp_connect_port)
+        self._tcp_connect_port = 0
         if self._power_monitor:
             self._power_monitor.Close()
             self._power_monitor = None
 
-    def _send(self, msg, report_errors=True):
+    def _send(self, msg, report_errors = True):
         """Connect to the device, send the given command, and then disconnect"""
-        if PowerTest.PORT == 0:
+        if self._tcp_connect_port == 0:
             # on first attempt to send a command, connect to device via any open port number,
             # forwarding that port to a local socket on the device via adb
             logging.debug("Seeking port for communication...")
             # discover an open port
             dummysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             dummysocket.bind(("localhost", 0))
-            (_, PowerTest.PORT) = dummysocket.getsockname()
+            (_, self._tcp_connect_port) = dummysocket.getsockname()
             dummysocket.close()
-            assert(PowerTest.PORT > 0)
+            assert(self._tcp_connect_port > 0)
+
             status = self.executeLocal("adb forward tcp:%d localabstract:%s" %
-                    (PowerTest.PORT, PowerTest.DOMAIN_NAME))
-            if report_errors:
-                self.reportErrorIf(status != 0, msg="Unable to forward requests to client over adb")
-            logging.info("Forwarding requests over local port %d" % PowerTest.PORT)
+                                       (self._tcp_connect_port, PowerTest.DOMAIN_NAME))
+            # If the status !=0, then the host machine is unable to
+            # forward requests to client over adb. Ending the test and logging error message
+            # to the console on the host.
+            self.endTestIfLostConnection(
+                status != 0,
+                "Unable to forward requests to client over adb")
+            logging.info("Forwarding requests over local port %d",
+                         self._tcp_connect_port)
 
         link = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
         try:
             logging.debug("Connecting to device...")
-            link.connect (("localhost", PowerTest.PORT))
+            link.connect(("localhost", self._tcp_connect_port))
             logging.debug("Connected.")
+        except socket.error as serr:
+            print "Socket connection error: ", serr
+            print "Finalizing and exiting the test"
+            self.endTestIfLostConnection(
+                report_errors,
+                "Unable to communicate with device: connection refused")
         except:
-            if report_errors:
-                self.reportErrorIf(True, msg="Unable to communicate with device: connection refused")
-        logging.debug("Sending '%s'" % msg)
+            print "Non socket-related exception at this block in _send(); re-raising now."
+            raise
+        logging.debug("Sending '%s'", msg)
         link.sendall(msg)
         logging.debug("Getting response...")
         response = link.recv(4096)
-        logging.debug("Got response '%s'" % response)
+        logging.debug("Got response '%s'", response)
         link.close()
         return response
 
     def queryDevice(self, query):
         """Post a yes/no query to the device, return True upon successful query, False otherwise"""
-        logging.info("Querying device with '%s'" % query)
+        logging.info("Querying device with '%s'", query)
         return self._send(query) == "OK"
 
     # TODO: abstract device communication (and string commands) into its own class
-    def executeOnDevice(self, cmd , reportErrors=True):
+    def executeOnDevice(self, cmd, reportErrors = True):
         """Execute a (string) command on the remote device"""
-        return self._send(cmd , reportErrors)
+        return self._send(cmd, reportErrors)
 
-    def executeLocal(self, cmd, check_status=True):
+    def executeLocal(self, cmd, check_status = True):
         """execute a shell command locally (on the host)"""
         from subprocess import call
-        status = call(cmd.split(' '))
+        status = call(cmd.split(" "))
         if status != 0 and check_status:
-            logging.error("Failed to execute \"%s\"" % cmd)
+            logging.error("Failed to execute \"%s\"", cmd)
         else:
-            logging.debug("Executed \"%s\"" % cmd)
+            logging.debug("Executed \"%s\"", cmd)
         return status
 
-    def reportErrorIf(self, condition, msg):
+    def reportErrorRaiseExceptionIf(self, condition, msg):
         """Report an error condition to the device if condition is True.
-        Will raise an exception on the device if condition is True"""
+        Will raise an exception on the device if condition is True.
+        Args:
+            condition: If true, this reports error
+            msg: Message related to exception
+        Raises:
+            A PowerTestException encapsulating the message provided in msg
+        """
         if condition:
             try:
                 logging.error("Exiting on error: %s" % msg)
-                self.executeOnDevice(PowerTest.REQUEST_RAISE % (self._current_test, msg), False)
+                self.executeOnDevice(PowerTest.REQUEST_RAISE % (self._current_test, msg),
+                                     reportErrors = True)
             except:
-
-                logging.error("Unable to communicate with device to report error: %s" % msg)
+                logging.error("Unable to communicate with device to report "
+                              "error: %s" % msg)
                 self.finalize()
                 sys.exit(msg)
-            raise Exception(msg)
+            raise PowerTestException(msg)
 
-    def setUsbEnabled(self, enabled, verbose=True):
+    def endTestIfLostConnection(self, lost_connection, error_message):
+        """
+        This function ends the test if lost_connection was true,
+        which indicates that the connection to the device was lost.
+        Args:
+            lost_connection: boolean variable, if True it indicates that
+                connection to device was lost and the test must be terminated.
+            error_message: String to print to the host console before exiting the test
+                (if lost_connection is True)
+        Returns:
+            None.
+        """
+        if lost_connection:
+            logging.error(error_message)
+            self.finalize()
+            sys.exit(error_message)
+
+    def setUsbEnabled(self, enabled, verbose = True):
         if enabled:
             val = 1
         else:
@@ -193,6 +298,7 @@
 
         # Sometimes command won't go through first time, particularly if immediately after a data
         # collection, so allow for retries
+        # TODO: Move this retry mechanism to the power monitor driver.
         status = self._power_monitor.GetStatus()
         while status is None and tries < 5:
             tries += 1
@@ -203,59 +309,230 @@
             status = self._power_monitor.GetStatus()
 
         if enabled:
-            if verbose: print("...USB enabled, waiting for device")
-            self.executeLocal ("adb wait-for-device")
-            if verbose: print("...device online")
+            if verbose:
+                print("...USB enabled, waiting for device")
+            self.executeLocal("adb wait-for-device")
+            if verbose:
+                print("...device online")
         else:
-            if verbose: logging.info("...USB disabled")
+            if verbose:
+                logging.info("...USB disabled")
         # re-establish port forwarding
-        if enabled and PowerTest.PORT > 0:
+        if enabled and self._tcp_connect_port > 0:
             status = self.executeLocal("adb forward tcp:%d localabstract:%s" %
-                                       (PowerTest.PORT, PowerTest.DOMAIN_NAME))
-            self.reportErrorIf(status != 0, msg="Unable to forward requests to client over adb")
+                                       (self._tcp_connect_port, PowerTest.DOMAIN_NAME))
+            self.reportErrorRaiseExceptionIf(status != 0, msg = "Unable to forward requests to client over adb")
 
-    def waitForScreenOff(self):
-        # disconnect of USB will cause screen to go on, so must wait (1 second more than screen off
-        # timeout)
-        if USE_STRICT_DELAY:
-            time.sleep(DELAY_SCREEN_OFF)
-            return
+    def computeBaselineState(self, measurements):
+        """
+        Args:
+            measurements: List of floats containing ampere draw measurements
+                taken from the monsoon power monitor.
+                Must be atleast 100 measurements long
+        Returns:
+            A tuple (isBaseline, mean_current) where isBaseline is a
+            boolean that is True only if the baseline state for the phone is
+            detected. mean_current is an estimate of the average baseline
+            current for the device, which is valid only if baseline state is
+            detected (if not, it is set to -1).
+        """
 
-        # need at least 100 sequential clean low-power measurements to know screen is off
-        THRESHOLD_COUNT_LOW_POWER = 100
-        CURRENT_LOW_POWER_THRESHOLD = 0.060  # Amps
-        TIMEOUT_SCREEN_OFF = 30 # this many tries at most
-        count_good = 0
-        tries = 0
-        print("Waiting for screen off and application processor in suspend mode...")
-        while count_good < THRESHOLD_COUNT_LOW_POWER:
-            measurements = self.collectMeasurements(THRESHOLD_COUNT_LOW_POWER,
-                                                      PowerTest.RATE_NOMINAL,
-                                                      ensure_screen_off=False,
-                                                      verbose=False)
-            count_good = len([m for m in measurements
-                               if m < CURRENT_LOW_POWER_THRESHOLD])
-            tries += 1
-            if count_good < THRESHOLD_COUNT_LOW_POWER and measurements:
-                print("Current usage: %.2f mAmps. Device is probably not in suspend mode.   Waiting..." %
-                      (1000.0*(sum(measurements)/len(measurements))))
-            if tries >= TIMEOUT_SCREEN_OFF:
-                # TODO: dump the state of sensor service to identify if there are features using sensors
-                self.reportErrorIf(tries>=TIMEOUT_SCREEN_OFF,
-                    msg="Unable to determine application processor suspend mode status.")
+        # Looks at the measurements to see if it is in baseline state
+        if len(measurements) < 100:
+            print(
+                "Need at least 100 measurements to determine if baseline state has"
+                " been reached")
+            return (False, -1)
+
+        # Assumption: At baseline state, the power profile is Gaussian distributed
+        # with low-variance around the mean current draw.
+        # Ideally we should find the mode from a histogram bin to find an estimated mean.
+        # Assuming here that the median is very close to this value; later we check that the
+        # variance of the samples is low enough to validate baseline.
+        sorted_measurements = sorted(measurements)
+        number_measurements = len(measurements)
+        if not number_measurements % 2:
+            median_measurement = (sorted_measurements[(number_measurements - 1) / 2] +
+                                  sorted_measurements[(number_measurements + 1) / 2]) / 2
+        else:
+            median_measurement = sorted_measurements[number_measurements / 2]
+
+        # Assume that at baseline state, a large fraction of power measurements
+        # are within +/- EXPECTED_AMPS_VARIATION_HALF_RANGE milliAmperes of
+        # the average baseline current. Find all such measurements in the
+        # sorted measurement vector.
+        left_index = (
+            bisect_left(
+                sorted_measurements,
+                median_measurement -
+                PowerTest.EXPECTED_AMPS_VARIATION_HALF_RANGE))
+        right_index = (
+            bisect_left(
+                sorted_measurements,
+                median_measurement +
+                PowerTest.EXPECTED_AMPS_VARIATION_HALF_RANGE))
+
+        average_baseline_amps = scipy.mean(
+            sorted_measurements[left_index: (right_index - 1)])
+
+        detected_baseline = True
+        # We enforce that a fraction of more than 'THRESHOLD_BASELINE_SAMPLES_FRACTION'
+        # of samples must be within +/- EXPECTED_AMPS_VARIATION_HALF_RANGE
+        # milliAmperes of the mean baseline current, which we have estimated as
+        # the median.
+        if ((right_index - left_index) < PowerTest.THRESHOLD_BASELINE_SAMPLES_FRACTION * len(
+                measurements)):
+            detected_baseline = False
+
+        # We check for the maximum limit of the expected baseline
+        if median_measurement > self._max_baseline_amps:
+            detected_baseline = False
+        if average_baseline_amps < 0:
+            print PowerTest.NEGATIVE_AMPERE_ERROR_MESSAGE
+            detected_baseline = False
+
+        print("%s baseline state" % ("Could detect" if detected_baseline else "Could NOT detect"))
+        print(
+            "median amps = %f, avg amps = %f, fraction of good samples = %f" %
+            (median_measurement, average_baseline_amps,
+             float(right_index - left_index) / len(measurements)))
+        if PowerTest.ENABLE_PLOTTING:
+            plt.plot(measurements)
+            plt.show()
+            print("To continue test, please close the plot window manually.")
+        return (detected_baseline, average_baseline_amps)
+
+    def isApInSuspendState(self, measurements_amps, nominal_max_amps, test_percentile):
+        """
+        This function detects AP suspend and display off state of phone
+        after a sensor has been registered.
+
+        Because the power profile can be very different between sensors and
+        even across builds, it is difficult to specify a tight threshold for
+        mean current draw or mandate that the power measurements must have low
+        variance. We use a criteria that allows for a certain fraction of
+        peaks in power spectrum and checks that test_percentile fraction of
+        measurements must be below the specified value nominal_max_amps
+        Args:
+            measurements_amps: amperes draw measurements from power monitor
+            test_percentile: the fraction of measurements we require to be below
+                             a specified amps value
+            nominal_max_amps: the specified value of the max current draw
+        Returns:
+            returns a boolean which is True if and only if the AP suspend and
+            display off state is detected
+        """
+        count_good = len([m for m in measurements_amps if m < nominal_max_amps])
+        count_negative = len([m for m in measurements_amps if m < 0])
+        if count_negative > 0:
+            print PowerTest.NEGATIVE_AMPERE_ERROR_MESSAGE
+            return False;
+        return count_good > test_percentile * len(measurements_amps)
+
+    def getBaselineState(self):
+        """This function first disables all sensors, then collects measurements
+        through the power monitor and continuously evaluates if baseline state
+        is reached. Once baseline state is detected, it returns a tuple with
+        status information. If baseline is not detected in a preset maximum
+        number of trials, it returns as well.
+
+        Returns:
+            Returns a tuple (isBaseline, mean_current) where isBaseline is a
+            boolean that is True only if the baseline state for the phone is
+            detected. mean_current is an estimate of the average baseline current
+            for the device, which is valid only if baseline state is detected
+            (if not, it is set to -1)
+        """
+        self.setPowerOn("ALL", False)
+        self.setUsbEnabled(False)
+        print("Waiting %d seconds for baseline state" % DELAY_SCREEN_OFF)
+        time.sleep(DELAY_SCREEN_OFF)
+
+        MEASUREMENT_DURATION_SECONDS_BASELINE_DETECTION = 5  # seconds
+        NUMBER_MEASUREMENTS_BASELINE_DETECTION = (
+            PowerTest.RATE_NOMINAL *
+            MEASUREMENT_DURATION_SECONDS_BASELINE_DETECTION)
+        NUMBER_MEASUREMENTS_BASELINE_VERIFICATION = (
+            NUMBER_MEASUREMENTS_BASELINE_DETECTION * 5)
+        MAX_TRIALS = 50
+
+        collected_baseline_measurements = False
+
+        for tries in xrange(MAX_TRIALS):
+            print("Trial number %d of %d..." % (tries, MAX_TRIALS))
+            measurements = self.collectMeasurements(
+                NUMBER_MEASUREMENTS_BASELINE_DETECTION, PowerTest.RATE_NOMINAL,
+                verbose = False)
+            if self.computeBaselineState(measurements)[0] is True:
+                collected_baseline_measurements = True
                 break
-        if DELAY_SCREEN_OFF:
-            # add additional delay time if necessary
-            time.sleep(DELAY_SCREEN_OFF)
-        print("...Screen off and device in suspend mode.")
 
-    def collectMeasurements(self, measurementCount, rate , ensure_screen_off=True, verbose=True,
-                             plot_data = False):
-        assert(measurementCount > 0)
-        decimate_by = self._native_hz / rate  or 1
-        if ensure_screen_off:
-            self.waitForScreenOff()
-            print ("Taking measurements...")
+        if collected_baseline_measurements:
+            print("Verifying baseline state over a longer interval "
+                  "in order to double check baseline state")
+            measurements = self.collectMeasurements(
+                NUMBER_MEASUREMENTS_BASELINE_VERIFICATION, PowerTest.RATE_NOMINAL,
+                verbose = False)
+            self.reportErrorRaiseExceptionIf(
+                not measurements, "No background measurements could be taken")
+            retval = self.computeBaselineState(measurements)
+            if retval[0]:
+                print("Verified baseline.")
+                if measurements and LOG_DATA_TO_FILE:
+                    with open("/tmp/cts-power-tests-background-data.log", "w") as f:
+                        for m in measurements:
+                            f.write("%.4f\n" % m)
+            return retval
+        else:
+            return (False, -1)
+
+    def waitForApSuspendMode(self):
+        """This function repeatedly collects measurements until AP suspend and display off
+        mode is detected. After a maximum number of trials, if this state is not reached, it
+        raises an error.
+        Returns:
+            boolean which is True if device was detected to be in suspend state
+        Raises:
+            Power monitor-related exception
+        """
+        print("waitForApSuspendMode(): Sleeping for %d seconds" % DELAY_SCREEN_OFF)
+        time.sleep(DELAY_SCREEN_OFF)
+
+        NUMBER_MEASUREMENTS = 200
+        # Maximum trials for which to collect measurements to get to Ap suspend
+        # state
+        MAX_TRIALS = 50
+
+        got_to_suspend_state = False
+        for count in xrange(MAX_TRIALS):
+            print ("waitForApSuspendMode(): Trial %d of %d" % (count, MAX_TRIALS))
+            measurements = self.collectMeasurements(NUMBER_MEASUREMENTS,
+                                                    PowerTest.RATE_NOMINAL,
+                                                    verbose = False)
+            if self.isApInSuspendState(
+                    measurements, PowerTest.MAX_PERCENTILE_AP_SCREEN_OFF_AMPS,
+                    PowerTest.PERCENTILE_MAX_AP_SCREEN_OFF):
+                got_to_suspend_state = True
+                break
+        self.reportErrorRaiseExceptionIf(
+            got_to_suspend_state is False,
+            msg = "Unable to determine application processor suspend mode status.")
+        print("Got to AP suspend state")
+        return got_to_suspend_state
+
+    def collectMeasurements(self, measurementCount, rate, verbose = True):
+        """Args:
+            measurementCount: Number of measurements to collect from the power
+                              monitor
+            rate: The integer frequency in Hertz at which to collect measurements from
+                  the power monitor
+        Returns:
+            A list containing measurements from the power monitor; that has the
+            requested count of the number of measurements at the specified rate
+        """
+        assert (measurementCount > 0)
+        decimate_by = self._native_hz / rate or 1
+
         self._power_monitor.StartDataCollection()
         sub_measurements = []
         measurements = []
@@ -273,47 +550,70 @@
                     tries = 0
                     sub_measurements.extend(additional)
                     while len(sub_measurements) >= decimate_by:
-                        sub_avg = sum(sub_measurements) / len(sub_measurements)
+                        sub_avg = sum(sub_measurements[0:decimate_by]) / decimate_by
                         measurements.append(sub_avg)
                         sub_measurements = sub_measurements[decimate_by:]
                         if verbose:
+                            # "\33[1A\33[2K" is a special Linux console control
+                            # sequence for moving to the previous line, and
+                            # erasing it; and reprinting new text on that
+                            # erased line.
                             sys.stdout.write("\33[1A\33[2K")
-                            print ("MEASURED[%d]: %f" % (len(measurements),measurements[-1]))
+                            print ("MEASURED[%d]: %f" % (len(measurements), measurements[-1]))
         finally:
             self._power_monitor.StopDataCollection()
 
-        self.reportErrorIf(measurementCount > len(measurements),
-                            "Unable to collect all requested measurements")
+        self.reportErrorRaiseExceptionIf(measurementCount > len(measurements),
+                           "Unable to collect all requested measurements")
         return measurements
 
-    def request_user_acknowledgment(self, msg):
+    def requestUserAcknowledgment(self, msg):
         """Post message to user on screen and wait for acknowledgment"""
         response = self.executeOnDevice(PowerTest.REQUEST_USER_RESPONSE % msg)
-        self.reportErrorIf(response != "OK", "Unable to request user acknowledgment")
+        self.reportErrorRaiseExceptionIf(
+            response != "OK", "Unable to request user acknowledgment")
 
-    def setTestResult (self, testname, condition, msg):
-        if condition is False:
-            val = "FAIL"
-        elif condition is True:
-            val = "PASS"
-        else:
-            val = condition
-        print ("Test %s : %s" % (testname, val))
-        response = self.executeOnDevice(PowerTest.REQUEST_SET_TEST_RESULT % (testname, val, msg))
-        self.reportErrorIf(response != "OK", "Unable to send test status to Verifier")
+    def setTestResult(self, test_name, test_result, test_message):
+        """
+        Reports the result of a test to the device
+        Args:
+            test_name: name of the test
+            test_result: Boolean result of the test (True means Pass)
+            test_message: Relevant message
+        """
+        print ("Test %s : %s" % (test_name, test_result))
+
+        response = (
+            self.executeOnDevice(
+                PowerTest.REQUEST_SET_TEST_RESULT %
+                (test_name, test_result, test_message)))
+        self.reportErrorRaiseExceptionIf(
+            response != "OK", "Unable to send test status to Verifier")
 
     def setPowerOn(self, sensor, powered_on):
         response = self.executeOnDevice(PowerTest.REQUEST_SENSOR_SWITCH %
-                                        ({True:"ON", False:"OFF"}[powered_on], sensor))
-        self.reportErrorIf(response == "ERR", "Unable to set sensor %s state" % sensor)
-        logging.info("Set %s %s" % (sensor, {True:"ON", False:"OFF"}[powered_on]))
+            (("ON" if powered_on else "OFF"), sensor))
+        self.reportErrorRaiseExceptionIf(
+            response == "ERR", "Unable to set sensor %s state" % sensor)
+        logging.info("Set %s %s", sensor, ("ON" if powered_on else "OFF"))
         return response
 
-    def runPowerTest(self, sensor, max_power_allowed, user_request = None):
-        if not signal_abort.empty():
-            sys.exit( signal_abort.get() )
-        self._current_test = "%s_Power_Test_While_%s" % (sensor,
-                                    {True:"Under_Motion", False:"Still"}[user_request is not None])
+    def runSensorPowerTest(
+            self, sensor, max_amperes_allowed, baseline_amps, user_request = None):
+        """
+        Runs power test for a specific sensor; i.e. measures the amperes draw
+        of the phone using monsoon, with the specified sensor mregistered
+        and the phone in suspend state; and verifies that the incremental
+        consumed amperes is within expected bounds.
+        Args:
+            sensor: The specified sensor for which to run the power test
+            max_amperes_allowed: Maximum ampere draw of the device with the
+                    sensor registered and device in suspend state
+            baseline_amps: The power draw of the device when it is in baseline
+                    state (no sensors registered, display off, AP asleep)
+        """
+        self._current_test = ("%s_Power_Test_While_%s" % (
+            sensor, ("Under_Motion" if user_request is not None else "Still")))
         try:
             print ("\n\n---------------------------------")
             if user_request is not None:
@@ -321,248 +621,299 @@
             else:
                 print ("Running power test on %s while device is still." % sensor)
             print ("---------------------------------")
-            response = self.executeOnDevice(PowerTest.REQUEST_SENSOR_AVAILABILITY % sensor)
+            response = self.executeOnDevice(
+                PowerTest.REQUEST_SENSOR_AVAILABILITY % sensor)
             if response == "UNAVAILABLE":
-                self.setTestResult(self._current_test, condition="SKIPPED",
-                    msg="Sensor %s not available on this platform"%sensor)
+                self.setTestResult(
+                    self._current_test, test_result = "SKIPPED",
+                    test_message = "Sensor %s not available on this platform" % sensor)
             self.setPowerOn("ALL", False)
             if response == "UNAVAILABLE":
-                self.setTestResult(self._current_test, condition="SKIPPED",
-                                   msg="Sensor %s not available on this device"%sensor)
+                self.setTestResult(
+                    self._current_test, test_result = "SKIPPED",
+                    test_message = "Sensor %s not available on this device" % sensor)
                 return
-
-            self.reportErrorIf(response != "OK", "Unable to set all sensor off")
-            if not signal_abort.empty():
-                sys.exit( signal_abort.get() )
+            self.reportErrorRaiseExceptionIf(response != "OK", "Unable to set all sensor off")
             self.executeOnDevice(PowerTest.REQUEST_SCREEN_OFF)
             self.setUsbEnabled(False)
-            print("Collecting background measurements...")
-            measurements = self.collectMeasurements( PowerTest.SAMPLE_COUNT_NOMINAL,
-                                                     PowerTest.RATE_NOMINAL,
-                                                     plot_data = True)
-            if measurements and LOG_DATA_TO_FILE:
-                with open( "/tmp/cts-power-tests-%s-%s-background-data.log"%(sensor,
-                   {True:"Under_Motion", False:"Still"}[user_request is not None] ),'w') as f:
-                    for m in measurements:
-                        f.write( "%.4f\n"%m)
-            self.reportErrorIf(not measurements, "No background measurements could be taken")
-            backgnd = sum(measurements) / len(measurements)
             self.setUsbEnabled(True)
             self.setPowerOn(sensor, True)
             if user_request is not None:
                 print("===========================================\n" +
                       "==> Please follow the instructions presented on the device\n" +
-                      "==========================================="
-                     )
-                self.request_user_acknowledgment(user_request)
+                      "===========================================")
+                self.requestUserAcknowledgment(user_request)
             self.executeOnDevice(PowerTest.REQUEST_SCREEN_OFF)
             self.setUsbEnabled(False)
-            self.reportErrorIf(response != "OK", "Unable to set sensor %s ON" % sensor)
+            self.reportErrorRaiseExceptionIf(
+                response != "OK", "Unable to set sensor %s ON" % sensor)
+
+            self.waitForApSuspendMode()
             print ("Collecting sensor %s measurements" % sensor)
             measurements = self.collectMeasurements(PowerTest.SAMPLE_COUNT_NOMINAL,
                                                     PowerTest.RATE_NOMINAL)
 
             if measurements and LOG_DATA_TO_FILE:
-                with open( "/tmp/cts-power-tests-%s-%s-sensor-data.log"%(sensor,
-                   {True:"Under_Motion", False:"Still"}[user_request is not None] ),'w') as f:
+                with open("/tmp/cts-power-tests-%s-%s-sensor-data.log" % (sensor,
+                    ("Under_Motion" if user_request is not None else "Still")), "w") as f:
                     for m in measurements:
-                        f.write( "%.4f\n"%m)
+                        f.write("%.4f\n" % m)
                     self.setUsbEnabled(True, verbose = False)
                     print("Saving raw data files to device...")
                     self.executeLocal("adb shell mkdir -p %s" % self._external_storage, False)
                     self.executeLocal("adb push %s %s/." % (f.name, self._external_storage))
                     self.setUsbEnabled(False, verbose = False)
-            self.reportErrorIf(not measurements, "No measurements could be taken for %s" % sensor)
+            self.reportErrorRaiseExceptionIf(
+                not measurements, "No measurements could be taken for %s" % sensor)
             avg = sum(measurements) / len(measurements)
-            squared = [(m-avg)*(m-avg) for m in measurements]
+            squared = [(m - avg) * (m - avg) for m in measurements]
 
-            import math
-            stddev = math.sqrt(sum(squared)/len(squared))
-            current_diff = avg - backgnd
+            stddev = math.sqrt(sum(squared) / len(squared))
+            current_diff = avg - baseline_amps
             self.setUsbEnabled(True)
             max_power = max(measurements) - avg
-            if current_diff <= max_power_allowed:
+            if current_diff <= max_amperes_allowed:
                 # TODO: fail the test of background > current
-                message = ("Draw is within limits. Current:%f Background:%f   Measured: %f Stddev: %f  Peak: %f")%\
-                             ( current_diff*1000.0, backgnd*1000.0, avg*1000.0, stddev*1000.0, max_power*1000.0)
+                message = (
+                              "Draw is within limits. Sensor delta:%f mAmp   Baseline:%f "
+                              "mAmp   Sensor: %f mAmp  Stddev : %f mAmp  Peak: %f mAmp") % (
+                              current_diff * 1000.0, baseline_amps * 1000.0, avg * 1000.0,
+                              stddev * 1000.0, max_power * 1000.0)
             else:
-                message = ("Draw is too high. Current:%f Background:%f   Measured: %f Stddev: %f  Peak: %f")%\
-                             ( current_diff*1000.0, backgnd*1000.0, avg*1000.0, stddev*1000.0, max_power*1000.0)
-            self.setTestResult( testname = self._current_test,
-                                condition = current_diff <= max_power_allowed,
-                                msg = message)
-            print("Result: "+message)
+                message = (
+                              "Draw is too high. Current:%f Background:%f   Measured: %f "
+                              "Stddev: %f  Peak: %f") % (
+                              current_diff * 1000.0, baseline_amps * 1000.0, avg * 1000.0,
+                              stddev * 1000.0, max_power * 1000.0)
+            self.setTestResult(
+                self._current_test,
+                ("PASS" if (current_diff <= max_amperes_allowed) else "FAIL"),
+                message)
+            print("Result: " + message)
         except:
-            import traceback
             traceback.print_exc()
-            self.setTestResult(self._current_test, condition="FAIL",
-                               msg="Exception occurred during run of test.")
-
+            self.setTestResult(self._current_test, test_result = "FAIL",
+                               test_message = "Exception occurred during run of test.")
+            raise
 
     @staticmethod
-    def run_tests():
+    def runTests(max_baseline_amps):
         testrunner = None
         try:
-            GENERIC_MOTION_REQUEST = "\n===> Please press Next and when the screen is off, keep " + \
-                "the device under motion with only tiny, slow movements until the screen turns " + \
-                "on again.\nPlease refrain from interacting with the screen or pressing any side " + \
-                "buttons while measurements are taken."
-            USER_STEPS_REQUEST = "\n===> Please press Next and when the screen is off, then move " + \
-                "the device to simulate step motion until the screen turns on again.\nPlease " + \
-                "refrain from interacting with the screen or pressing any side buttons while " + \
-                "measurements are taken."
-            testrunner = PowerTest()
-            testrunner.executeOnDevice(PowerTest.REQUEST_SHOW_MESSAGE % "Connected.  Running tests...")
-            testrunner.runPowerTest("SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_POWER, user_request = GENERIC_MOTION_REQUEST)
-            testrunner.runPowerTest("STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_POWER, user_request = USER_STEPS_REQUEST)
-            testrunner.runPowerTest("STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_POWER, user_request = USER_STEPS_REQUEST)
-            testrunner.runPowerTest("ACCELEROMETER", PowerTest.MAX_ACCEL_POWER, user_request = GENERIC_MOTION_REQUEST)
-            testrunner.runPowerTest("MAGNETIC_FIELD", PowerTest.MAX_MAG_POWER, user_request = GENERIC_MOTION_REQUEST)
-            testrunner.runPowerTest("GYROSCOPE", PowerTest.MAX_GYRO_POWER, user_request = GENERIC_MOTION_REQUEST)
-            testrunner.runPowerTest("ACCELEROMETER", PowerTest.MAX_ACCEL_POWER, user_request = None)
-            testrunner.runPowerTest("MAGNETIC_FIELD", PowerTest.MAX_MAG_POWER, user_request = None)
-            testrunner.runPowerTest("GYROSCOPE", PowerTest.MAX_GYRO_POWER, user_request = None)
-            testrunner.runPowerTest("SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_POWER, user_request = None)
-            testrunner.runPowerTest("STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_POWER, user_request = None)
-            testrunner.runPowerTest("STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_POWER, user_request = None)
+            GENERIC_MOTION_REQUEST = ("\n===> Please press Next and when the "
+                "screen is off, keep the device under motion with only tiny, "
+                "slow movements until the screen turns on again.\nPlease "
+                "refrain from interacting with the screen or pressing any side "
+                "buttons while measurements are taken.")
+            USER_STEPS_REQUEST = ("\n===> Please press Next and when the "
+                "screen is off, then move the device to simulate step motion "
+                "until the screen turns on again.\nPlease refrain from "
+                "interacting with the screen or pressing any side buttons "
+                "while measurements are taken.")
+            testrunner = PowerTest(max_baseline_amps)
+            testrunner.executeOnDevice(
+                PowerTest.REQUEST_SHOW_MESSAGE % "Connected.  Running tests...")
+            is_baseline_success, baseline_amps = testrunner.getBaselineState()
+
+            if is_baseline_success:
+                testrunner.setUsbEnabled(True)
+                # TODO: Enable testing a single sensor
+                testrunner.runSensorPowerTest(
+                    "SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_AMPS, baseline_amps,
+                    user_request = GENERIC_MOTION_REQUEST)
+                testrunner.runSensorPowerTest(
+                    "STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_AMPS, baseline_amps,
+                    user_request = USER_STEPS_REQUEST)
+                testrunner.runSensorPowerTest(
+                    "STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_AMPS, baseline_amps,
+                    user_request = USER_STEPS_REQUEST)
+                testrunner.runSensorPowerTest(
+                    "ACCELEROMETER", PowerTest.MAX_ACCEL_AMPS, baseline_amps,
+                    user_request = GENERIC_MOTION_REQUEST)
+                testrunner.runSensorPowerTest(
+                    "MAGNETIC_FIELD", PowerTest.MAX_MAG_AMPS, baseline_amps,
+                    user_request = GENERIC_MOTION_REQUEST)
+                testrunner.runSensorPowerTest(
+                    "GYROSCOPE", PowerTest.MAX_GYRO_AMPS, baseline_amps,
+                    user_request = GENERIC_MOTION_REQUEST)
+                testrunner.runSensorPowerTest(
+                    "ACCELEROMETER", PowerTest.MAX_ACCEL_AMPS, baseline_amps,
+                    user_request = None)
+                testrunner.runSensorPowerTest(
+                    "MAGNETIC_FIELD", PowerTest.MAX_MAG_AMPS, baseline_amps,
+                    user_request = None)
+                testrunner.runSensorPowerTest(
+                    "GYROSCOPE", PowerTest.MAX_GYRO_AMPS, baseline_amps,
+                    user_request = None)
+                testrunner.runSensorPowerTest(
+                    "SIGNIFICANT_MOTION", PowerTest.MAX_SIGMO_AMPS, baseline_amps,
+                    user_request = None)
+                testrunner.runSensorPowerTest(
+                    "STEP_DETECTOR", PowerTest.MAX_STEP_DETECTOR_AMPS, baseline_amps,
+                    user_request = None)
+                testrunner.runSensorPowerTest(
+                    "STEP_COUNTER", PowerTest.MAX_STEP_COUNTER_AMPS, baseline_amps,
+                    user_request = None)
+            else:
+                print("Could not get to baseline state. This is either because "
+                      "in several trials, the monitor could not measure a set "
+                      "of power measurements that had the specified low "
+                      "variance or the mean measurements were below the "
+                      "expected value. None of the sensor power measurement "
+                      " tests were performed due to not being able to detect "
+                      "baseline state. Please re-run the power tests.")
+        except KeyboardInterrupt:
+            print "Keyboard interrupt from user."
+            raise
         except:
             import traceback
             traceback.print_exc()
         finally:
-            signal_exit_q.put(0) # anything will signal thread to terminate
             logging.info("TESTS COMPLETE")
             if testrunner:
                 try:
                     testrunner.finalize()
                 except socket.error:
-                    sys.exit("============================\nUnable to connect to device under " + \
-                             "test. Make sure the device is connected via the usb pass-through, " + \
-                             "the CtsVerifier app is running the SensorPowerTest on the device, " + \
-                             "and USB pass-through is enabled.\n===========================")
-
+                    sys.exit(
+                        "===================================================\n"
+                        "Unable to connect to device under test. Make sure \n"
+                        "the device is connected via the usb pass-through, \n"
+                        "the CtsVerifier app is running the SensorPowerTest on \n"
+                        "the device, and USB pass-through is enabled.\n"
+                        "===================================================")
 
 def main(argv):
-  """ Simple command-line interface for a power test application."""
-  useful_flags = ["voltage", "status", "usbpassthrough",
-                  "samples", "current", "log", "power_monitor"]
-  if not [f for f in useful_flags if FLAGS.get(f, None) is not None]:
-    print __doc__.strip()
-    print FLAGS.MainModuleHelp()
-    return
+    """ Simple command-line interface for a power test application."""
+    useful_flags = ["voltage", "status", "usbpassthrough",
+                    "samples", "current", "log", "power_monitor"]
+    if not [f for f in useful_flags if FLAGS.get(f, None) is not None]:
+        print __doc__.strip()
+        print FLAGS.MainModuleHelp()
+        return
 
-  if FLAGS.avg and FLAGS.avg < 0:
-    loggign.error("--avg must be greater than 0")
-    return
+    if FLAGS.avg and FLAGS.avg < 0:
+        logging.error("--avg must be greater than 0")
+        return
 
-  if FLAGS.voltage is not None:
-    if FLAGS.voltage > 5.5:
-        print("!!WARNING: Voltage higher than typical values!!!")
-    try:
-        response = raw_input("Voltage of %.3f requested.  Confirm this is correct (Y/N)"%FLAGS.voltage)
-        if response.upper() != "Y":
-            sys.exit("Aborting")
-    except:
-        sys.exit("Aborting.")
+    if FLAGS.voltage is not None:
+        if FLAGS.voltage > 5.5:
+            print("!!WARNING: Voltage higher than typical values!!!")
+        try:
+            response = raw_input(
+                "Voltage of %.3f requested.  Confirm this is correct (Y/N)" %
+                FLAGS.voltage)
+            if response.upper() != "Y":
+                sys.exit("Aborting")
+        except:
+            sys.exit("Aborting.")
 
-  if not FLAGS.power_monitor:
-      sys.exit("You must specify a '--power_monitor' option to specify which power monitor type " + \
-               "you are using.\nOne of:\n  \n  ".join(available_monitors))
-  power_monitors = do_import('power_monitors.%s' % FLAGS.power_monitor)
-  try:
-      mon = power_monitors.Power_Monitor(device=FLAGS.device)
-  except:
-      import traceback
-      traceback.print_exc()
-      sys.exit("No power monitors found")
-
-  if FLAGS.voltage is not None:
-
-    if FLAGS.ramp is not None:
-      mon.RampVoltage(mon.start_voltage, FLAGS.voltage)
-    else:
-      mon.SetVoltage(FLAGS.voltage)
-
-  if FLAGS.current is not None:
-    mon.SetMaxCurrent(FLAGS.current)
-
-  if FLAGS.status:
-    items = sorted(mon.GetStatus().items())
-    print "\n".join(["%s: %s" % item for item in items])
-
-  if FLAGS.usbpassthrough:
-    if FLAGS.usbpassthrough == 'off':
-      mon.SetUsbPassthrough(0)
-    elif FLAGS.usbpassthrough == 'on':
-      mon.SetUsbPassthrough(1)
-    elif FLAGS.usbpassthrough == 'auto':
-      mon.SetUsbPassthrough(2)
-    else:
-      mon.Close()
-      sys.exit('bad pass-through flag: %s' % FLAGS.usbpassthrough)
-
-  if FLAGS.samples:
-    # Make sure state is normal
-    mon.StopDataCollection()
-    status = mon.GetStatus()
-    native_hz = status["sampleRate"] * 1000
-
-    # Collect and average samples as specified
-    mon.StartDataCollection()
-
-    # In case FLAGS.hz doesn't divide native_hz exactly, use this invariant:
-    # 'offset' = (consumed samples) * FLAGS.hz - (emitted samples) * native_hz
-    # This is the error accumulator in a variation of Bresenham's algorithm.
-    emitted = offset = 0
-    collected = []
-    history_deque = collections.deque()  # past n samples for rolling average
-
-    try:
-      last_flush = time.time()
-      while emitted < FLAGS.samples or FLAGS.samples == -1:
-        # The number of raw samples to consume before emitting the next output
-        need = (native_hz - offset + FLAGS.hz - 1) / FLAGS.hz
-        if need > len(collected):  # still need more input samples
-          samples = mon.CollectData()
-          if not samples: break
-          collected.extend(samples)
-        else:
-          # Have enough data, generate output samples.
-          # Adjust for consuming 'need' input samples.
-          offset += need * FLAGS.hz
-          while offset >= native_hz:  # maybe multiple, if FLAGS.hz > native_hz
-            this_sample = sum(collected[:need]) / need
-
-            if FLAGS.timestamp: print int(time.time()),
-
-            if FLAGS.avg:
-              history_deque.appendleft(this_sample)
-              if len(history_deque) > FLAGS.avg: history_deque.pop()
-              print "%f %f" % (this_sample,
-                               sum(history_deque) / len(history_deque))
-            else:
-              print "%f" % this_sample
-            sys.stdout.flush()
-
-            offset -= native_hz
-            emitted += 1  # adjust for emitting 1 output sample
-          collected = collected[need:]
-          now = time.time()
-          if now - last_flush >= 0.99:  # flush every second
-            sys.stdout.flush()
-            last_flush = now
-    except KeyboardInterrupt:
-      print("interrupted")
-      return 1
-    finally:
-      mon.Close()
-    return 0
-
-  if FLAGS.run:
     if not FLAGS.power_monitor:
-        sys.exit("When running power tests, you must specify which type of power monitor to use" +
-                 " with '--power_monitor <type of power monitor>'")
-    PowerTest.run_tests()
+        sys.exit(
+            "You must specify a '--power_monitor' option to specify which power "
+            "monitor type " +
+            "you are using.\nOne of:\n  \n  ".join(available_monitors))
+    power_monitors = do_import("power_monitors.%s" % FLAGS.power_monitor)
+    try:
+        mon = power_monitors.Power_Monitor(device = FLAGS.device)
+    except:
+        import traceback
 
+        traceback.print_exc()
+        sys.exit("No power monitors found")
+
+    if FLAGS.voltage is not None:
+
+        if FLAGS.ramp is not None:
+            mon.RampVoltage(mon.start_voltage, FLAGS.voltage)
+        else:
+            mon.SetVoltage(FLAGS.voltage)
+
+    if FLAGS.current is not None:
+        mon.SetMaxCurrent(FLAGS.current)
+
+    if FLAGS.status:
+        items = sorted(mon.GetStatus().items())
+        print "\n".join(["%s: %s" % item for item in items])
+
+    if FLAGS.usbpassthrough:
+        if FLAGS.usbpassthrough == "off":
+            mon.SetUsbPassthrough(0)
+        elif FLAGS.usbpassthrough == "on":
+            mon.SetUsbPassthrough(1)
+        elif FLAGS.usbpassthrough == "auto":
+            mon.SetUsbPassthrough(2)
+        else:
+            mon.Close()
+            sys.exit("bad pass-through flag: %s" % FLAGS.usbpassthrough)
+
+    if FLAGS.samples:
+        # Make sure state is normal
+        mon.StopDataCollection()
+        status = mon.GetStatus()
+        native_hz = status["sampleRate"] * 1000
+
+        # Collect and average samples as specified
+        mon.StartDataCollection()
+
+        # In case FLAGS.hz doesn't divide native_hz exactly, use this invariant:
+        # 'offset' = (consumed samples) * FLAGS.hz - (emitted samples) * native_hz
+        # This is the error accumulator in a variation of Bresenham's algorithm.
+        emitted = offset = 0
+        collected = []
+        history_deque = collections.deque()  # past n samples for rolling average
+
+        # TODO: Complicated lines of code below. Refactoring needed
+        try:
+            last_flush = time.time()
+            while emitted < FLAGS.samples or FLAGS.samples == -1:
+                # The number of raw samples to consume before emitting the next output
+                need = (native_hz - offset + FLAGS.hz - 1) / FLAGS.hz
+                if need > len(collected):  # still need more input samples
+                    samples = mon.CollectData()
+                    if not samples: break
+                    collected.extend(samples)
+                else:
+                    # Have enough data, generate output samples.
+                    # Adjust for consuming 'need' input samples.
+                    offset += need * FLAGS.hz
+                    while offset >= native_hz:  # maybe multiple, if FLAGS.hz > native_hz
+                        this_sample = sum(collected[:need]) / need
+
+                        if FLAGS.timestamp: print int(time.time()),
+
+                        if FLAGS.avg:
+                            history_deque.appendleft(this_sample)
+                            if len(history_deque) > FLAGS.avg: history_deque.pop()
+                            print "%f %f" % (this_sample,
+                                             sum(history_deque) / len(history_deque))
+                        else:
+                            print "%f" % this_sample
+                        sys.stdout.flush()
+
+                        offset -= native_hz
+                        emitted += 1  # adjust for emitting 1 output sample
+                    collected = collected[need:]
+                    now = time.time()
+                    if now - last_flush >= 0.99:  # flush every second
+                        sys.stdout.flush()
+                        last_flush = now
+        except KeyboardInterrupt:
+            print("interrupted")
+            return 1
+        finally:
+            mon.Close()
+        return 0
+
+    if FLAGS.run:
+        if not FLAGS.power_monitor:
+            sys.exit(
+                "When running power tests, you must specify which type of power "
+                "monitor to use" +
+                " with '--power_monitor <type of power monitor>'")
+        try:
+            PowerTest.runTests(FLAGS.max_baseline_amps)
+        except KeyboardInterrupt:
+            print "Keyboard interrupt from user"
 
 if __name__ == "__main__":
     flags.DEFINE_boolean("status", None, "Print power meter status")
@@ -581,5 +932,6 @@
     flags.DEFINE_boolean("log", False, "Log progress to a file or not")
     flags.DEFINE_boolean("run", False, "Run the test suite for power")
     flags.DEFINE_string("power_monitor", None, "Type of power monitor to use")
+    flags.DEFINE_float("max_baseline_amps", 0.005,
+                       "Set maximum baseline current for device being tested")
     sys.exit(main(FLAGS(sys.argv)))
-
diff --git a/apps/CtsVerifier/res/drawable-hdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-hdpi/ic_corp_icon.png
new file mode 100644
index 0000000..06c5135
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-mdpi/ic_corp_icon.png
new file mode 100644
index 0000000..79372b2
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xhdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-xhdpi/ic_corp_icon.png
new file mode 100644
index 0000000..3626c7d
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xhdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxhdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-xxhdpi/ic_corp_icon.png
new file mode 100644
index 0000000..d33319f
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxhdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxxhdpi/ic_corp_icon.png b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_corp_icon.png
new file mode 100644
index 0000000..359e210
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_corp_icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/layout/byod_custom_view_badged_icons.xml b/apps/CtsVerifier/res/layout/byod_custom_view.xml
similarity index 89%
rename from apps/CtsVerifier/res/layout/byod_custom_view_badged_icons.xml
rename to apps/CtsVerifier/res/layout/byod_custom_view.xml
index 1a29f65..00c9ad9 100644
--- a/apps/CtsVerifier/res/layout/byod_custom_view_badged_icons.xml
+++ b/apps/CtsVerifier/res/layout/byod_custom_view.xml
@@ -28,12 +28,10 @@
         <TextView android:id="@+id/message"
                 style="@style/InstructionsSmallFont"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/provisioning_byod_workapps_visible_instruction" />
+                android:layout_height="wrap_content" />
     </ScrollView>
 
     <ImageView android:id="@+id/sample_icon"
-            android:src="@drawable/badged_icon"
             android:layout_width="56dip"
             android:layout_height="56dip"
             android:layout_gravity="center_horizontal" />
diff --git a/apps/CtsVerifier/res/layout/fs_info.xml b/apps/CtsVerifier/res/layout/fs_info.xml
index 3fe6815..ea02fee 100644
--- a/apps/CtsVerifier/res/layout/fs_info.xml
+++ b/apps/CtsVerifier/res/layout/fs_info.xml
@@ -13,62 +13,44 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent" android:layout_height="wrap_content">
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent" android:layout_height="fill_parent">
+    <GridLayout
+        android:columnCount="2"
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content">
 
-    <ImageView android:id="@+id/fs_legend_good_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_good"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true" />
-    <TextView android:id="@+id/fs_legend_good_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_good"
-        android:layout_toRightOf="@id/fs_legend_good_image"
-        android:layout_alignTop="@id/fs_legend_good_image"
-        android:layout_marginLeft="3dip" />
+        <ImageView android:id="@+id/fs_legend_good_image"
+            android:src="@drawable/fs_good"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_good_text"
+            android:text="@string/fs_legend_good"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
 
-    <ImageView android:id="@+id/fs_legend_indeterminate_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_indeterminate"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/fs_legend_good_image" />
-    <TextView android:id="@+id/fs_legend_indeterminate_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_indeterminate"
-        android:layout_toRightOf="@id/fs_legend_indeterminate_image"
-        android:layout_alignTop="@id/fs_legend_indeterminate_image"
-        android:layout_marginLeft="3dip" />
+        <ImageView android:id="@+id/fs_legend_indeterminate_image"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_indeterminate_text"
+            android:text="@string/fs_legend_indeterminate"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
 
-    <ImageView android:id="@+id/fs_legend_warning_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_warning"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/fs_legend_indeterminate_image" />
-    <TextView android:id="@+id/fs_legend_warning_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_warning"
-        android:layout_toRightOf="@id/fs_legend_warning_image"
-        android:layout_alignTop="@id/fs_legend_warning_image"
-        android:layout_marginLeft="3dip" />
+        <ImageView android:id="@+id/fs_legend_warning_image"
+            android:src="@drawable/fs_warning"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_warning_text"
+            android:text="@string/fs_legend_warning"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
 
-    <ImageView android:id="@+id/fs_legend_error_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_error"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/fs_legend_warning_image" />
-    <TextView android:id="@+id/fs_legend_error_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_error"
-        android:layout_toRightOf="@id/fs_legend_error_image"
-        android:layout_alignTop="@id/fs_legend_error_image"
-        android:layout_marginLeft="3dip" />
-</RelativeLayout>
+        <ImageView android:id="@+id/fs_legend_error_image"
+            android:src="@drawable/fs_error"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_error_text"
+            android:text="@string/fs_legend_error"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
+    </GridLayout>
+</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/screen_pinning.xml b/apps/CtsVerifier/res/layout/screen_pinning.xml
new file mode 100644
index 0000000..f1d7b65
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/screen_pinning.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <TextView
+        android:id="@+id/error_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true" >
+
+        <RelativeLayout
+            android:id="@+id/instructions_group"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" >
+
+            <LinearLayout
+                android:id="@+id/instructions_list"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical" >
+
+            </LinearLayout>
+
+            <Button
+                android:id="@+id/next_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_below="@id/instructions_list"
+                android:text="@string/next_button_text" />
+
+        </RelativeLayout>
+    </ScrollView>
+
+    <include
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        layout="@layout/pass_fail_buttons" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index af269c6..b92bca9 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -443,6 +443,10 @@
     <string name="nfc_hce_payment_dynamic_aids_reader">Dynamic payment AIDs (Reader)</string>
     <string name="nfc_hce_payment_dynamic_aids_help">This test tries to register dynamic AIDs for a payment service.</string>
 
+    <string name="nfc_hce_large_num_aids_emulator">Large number of AIDs (Emulator)</string>
+    <string name="nfc_hce_large_num_aids_reader">Large number of AIDs (Reader)</string>
+    <string name="nfc_hce_large_num_aids_help">This test tries to register a large number of different AIDs, to make sure there are no limitations on the maximum amount of HCE apps on the device. Note that this test may take a few seconds to complete; please be patient.</string>
+
     <string name="nfc_hce_payment_prefix_aids_emulator">Payment prefix AIDs (Emulator)</string>
     <string name="nfc_hce_payment_prefix_aids_reader">Payment prefix AIDs (Reader)</string>
     <string name="nfc_hce_payment_prefix_aids_help">This test statically registers prefix AIDs for a payment service.</string>
@@ -1100,6 +1104,20 @@
     <string name="widget_pass">Pass</string>
     <string name="widget_fail">Fail</string>
 
+    <string name="provisioning_byod_nonmarket_allow">Enable non-market apps</string>
+    <string name="provisioning_byod_nonmarket_allow_info">
+        This test verifies that non-market apps can be installed if permitted.\n
+        1. A package installation UI should appear.\n
+        2. Accept the package and verify that it installs.
+    </string>
+
+    <string name="provisioning_byod_nonmarket_deny">Disable non-market apps</string>
+    <string name="provisioning_byod_nonmarket_deny_info">
+        This test verifies that non-market apps cannot be installed unless permitted.\n
+        1. A package installation UI should appear.\n
+        2. Verify that the installation of the package is refused.
+    </string>
+
     <!-- Strings for DeskClock -->
     <string name="deskclock_tests">Alarms and Timers Tests</string>
     <string name="deskclock_tests_info">
@@ -1269,6 +1287,13 @@
         \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_work_notification">Work notification is badged</string>
+    <string name="provisioning_byod_work_notification_instruction">
+        Please press the Go button to trigger a notification.\n
+        \n
+        Verify that the notification is badged (see sample badge below). Then mark this test accordingly.
+    </string>
+    <string name="provisioning_byod_work_notification_title">This is a work notification</string>
     <string name="provisioning_byod_profile_visible_instruction">
         Please press the Go button to open the Settings page.
         Navigate to Accounts and confirm that:\n
@@ -1422,4 +1447,18 @@
 
     <!-- A list of fully-qualified test classes that should not be run. -->
     <string-array name="disabled_tests" />
+
+    <!-- Strings for screen pinning test -->
+    <string name="screen_pinning_test">Screen Pinning Test</string>
+    <string name="screen_pin_instructions">Pressing next will prompt you to enter screen pinning, allow this app to enter screen pinning.</string>
+    <string name="screen_pin_check_pinned">Press Next to verify the app is pinned.</string>
+    <string name="screen_pin_no_exit">Try to leave the app without unpinning the screen. Press next once you have verified you cannot leave.</string>
+    <string name="screen_pin_exit">Use interactions defined by your device to unpin such as long pressing the back and overview button, then press next.</string>
+    <string name="screen_pinning_done">All tests completed successfully.</string>
+
+    <string name="error_screen_no_longer_pinned">The screen was no longer pinned.</string>
+    <string name="error_screen_already_pinned">Cannot start the test with the screen already pinned.</string>
+    <string name="error_screen_pinning_did_not_start">Screen was not pinned.</string>
+    <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>
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
index ebddf4f..6b9316f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
@@ -105,18 +105,20 @@
 
     private String mTestParent;
 
-    public ManifestTestListAdapter(Context context, String testParent) {
+    public ManifestTestListAdapter(Context context, String testParent, String[] disabledTestArray) {
         super(context);
         mContext = context;
         mTestParent = testParent;
-
-        String[] disabledTestArray = context.getResources().getStringArray(R.array.disabled_tests);
         mDisabledTests = new HashSet<>(disabledTestArray.length);
         for (int i = 0; i < disabledTestArray.length; i++) {
             mDisabledTests.add(disabledTestArray[i]);
         }
     }
 
+    public ManifestTestListAdapter(Context context, String testParent) {
+        this(context, testParent, context.getResources().getStringArray(R.array.disabled_tests));
+    }
+
     @Override
     protected List<TestListItem> getRows() {
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
index a6489c1..bf3484e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
@@ -59,6 +59,7 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.ble_power_level_name,
                          R.string.ble_power_level_info, -1);
+        getPassButton().setEnabled(false);
 
         mTimerText = (TextView)findViewById(R.id.ble_timer);
         mTimer = new CountDownTimer(REFRESH_MAC_TIME, 1000) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
index 9895f02..56d73aa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
@@ -20,7 +20,12 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
+import android.bluetooth.BluetoothAdapter;
 import android.os.Bundle;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class BluetoothTestActivity extends PassFailButtons.TestListActivity {
 
@@ -31,6 +36,32 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.bluetooth_test, R.string.bluetooth_test_info, -1);
 
-        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
+        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (bluetoothAdapter == null) {
+            Toast.makeText(this, "bluetooth not supported", Toast.LENGTH_SHORT);
+            return;
+        }
+
+        List<String> disabledTestArray = new ArrayList<String>();
+        for (String s : this.getResources().getStringArray(R.array.disabled_tests)) {
+            disabledTestArray.add(s);
+        }
+        if (!this.getPackageManager().hasSystemFeature("android.hardware.bluetooth_le")) {
+            disabledTestArray.add(
+                  "com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity");
+            disabledTestArray.add(
+                  "com.android.cts.verifier.bluetooth.BleScannerTestActivity");
+            disabledTestArray.add(
+                  "com.android.cts.verifier.bluetooth.BleClientTestActivity");
+            disabledTestArray.add(
+                  "com.android.cts.verifier.bluetooth.BleServerStartActivity");
+        } else if (!bluetoothAdapter.isMultipleAdvertisementSupported()) {
+            disabledTestArray.add(
+                  "com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity");
+            disabledTestArray.add(
+                  "com.android.cts.verifier.bluetooth.BleServerStartActivity");
+        }
+        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+                disabledTestArray.toArray(new String[disabledTestArray.size()])));
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
index 2541142..2011314 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
@@ -166,7 +166,9 @@
                         int length = w * bytesPerPixel;
                         buffer.get(data, offset, length);
                         // Advance buffer the remainder of the row stride
-                        buffer.position(buffer.position() + rowStride - length);
+                        if (row < h - 1) {
+                            buffer.position(buffer.position() + rowStride - length);
+                        }
                         offset += length;
                     } else {
                         // Generic case: should work for any pixelStride but slower.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index b697be8..057d00d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -75,6 +75,9 @@
     private TestItem mDeviceAdminVisibleTest;
     private TestItem mWorkAppVisibleTest;
     private TestItem mCrossProfileIntentFiltersTest;
+    private TestItem mDisableNonMarketTest;
+    private TestItem mEnableNonMarketTest;
+    private TestItem mWorkNotificationBadgedTest;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -168,16 +171,27 @@
          * To keep the image in this test up to date, use the instructions in
          * {@link ByodIconSamplerActivity}.
          */
-        mWorkAppVisibleTest = new TestItem(this, R.string.provisioning_byod_workapps_visible,
+        mWorkAppVisibleTest = new TestItemWithIcon(this,
+                R.string.provisioning_byod_workapps_visible,
                 R.string.provisioning_byod_profile_visible_instruction,
-                new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)) {
-            @Override
-            public View getCustomView() {
-                LayoutInflater layoutInflater = LayoutInflater.from(getApplicationContext());
-                return layoutInflater.inflate(R.layout.byod_custom_view_badged_icons,
-                        null /* root */);
-            }
-        };
+                new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
+                R.drawable.badged_icon);
+
+        mWorkNotificationBadgedTest = new TestItemWithIcon(this,
+                R.string.provisioning_byod_work_notification,
+                R.string.provisioning_byod_work_notification_instruction,
+                new Intent(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION),
+                R.drawable.ic_corp_icon);
+
+        mDisableNonMarketTest = new TestItem(this, R.string.provisioning_byod_nonmarket_deny,
+                R.string.provisioning_byod_nonmarket_deny_info,
+                new Intent(ByodHelperActivity.ACTION_INSTALL_APK)
+                        .putExtra(ByodHelperActivity.EXTRA_ALLOW_NON_MARKET_APPS, false));
+
+        mEnableNonMarketTest = new TestItem(this, R.string.provisioning_byod_nonmarket_allow,
+                R.string.provisioning_byod_nonmarket_allow_info,
+                new Intent(ByodHelperActivity.ACTION_INSTALL_APK)
+                        .putExtra(ByodHelperActivity.EXTRA_ALLOW_NON_MARKET_APPS, true));
 
         Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
         Intent chooser = Intent.createChooser(intent, getResources().getString(R.string.provisioning_cross_profile_chooser));
@@ -190,7 +204,10 @@
         mTests.add(mProfileVisibleTest);
         mTests.add(mDeviceAdminVisibleTest);
         mTests.add(mWorkAppVisibleTest);
+        mTests.add(mWorkNotificationBadgedTest);
         mTests.add(mCrossProfileIntentFiltersTest);
+        mTests.add(mDisableNonMarketTest);
+        mTests.add(mEnableNonMarketTest);
     }
 
     @Override
@@ -208,12 +225,14 @@
                 .setPositiveButton(R.string.pass_button_text, new AlertDialog.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
+                        clearRemainingState(test);
                         setTestResult(test, TestResult.Passed);
                     }
                 })
                 .setNegativeButton(R.string.fail_button_text, new AlertDialog.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
+                        clearRemainingState(test);
                         setTestResult(test, TestResult.Failed);
                     }
                 });
@@ -234,6 +253,14 @@
         });
     }
 
+    private void clearRemainingState(final TestItem test) {
+        if (WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION.equals(
+                test.getManualTestIntent().getAction())) {
+            ByodFlowTestActivity.this.startActivity(new Intent(
+                    WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION));
+        }
+    }
+
     private void setTestResult(TestItem test, TestResult result) {
         test.setPassFailState(result);
 
@@ -292,6 +319,10 @@
                 this, ByodHelperActivity.class),
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                 PackageManager.DONT_KILL_APP);
+        getPackageManager().setComponentEnabledSetting(new ComponentName(
+                this, WorkNotificationTestActivity.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
     }
 
     private void showToast(int messageId) {
@@ -361,6 +392,29 @@
         }
     }
 
+    static class TestItemWithIcon extends TestItem {
+
+        private int mImageResId;
+        private Context mContext;
+
+        public TestItemWithIcon(Context context, int nameResId, int testInstructionResId,
+                Intent testIntent, int imageResId) {
+            super(context, nameResId, testInstructionResId, testIntent);
+            mContext = context;
+            mImageResId = imageResId;
+        }
+
+        @Override
+        public View getCustomView() {
+            LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+            View view = layoutInflater.inflate(R.layout.byod_custom_view,
+                    null /* root */);
+            ((ImageView) view.findViewById(R.id.sample_icon)).setImageResource(mImageResId);
+            ((TextView) view.findViewById(R.id.message)).setText(getManualTestInstruction());
+            return view;
+        }
+    }
+
     static class TestAdapter extends ArrayAdapter<TestItem> {
 
         public TestAdapter(Context context) {
@@ -389,5 +443,4 @@
             return view;
         }
     }
-
 }
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 5dac4bd..277324c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -24,10 +24,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.util.Log;
 import android.widget.Toast;
 
+import static android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS;
+
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.managedprovisioning.ByodFlowTestActivity.TestResult;
 
@@ -53,17 +57,33 @@
 
     public static final String EXTRA_PROVISIONED = "extra_provisioned";
 
-    private ComponentName mAdminReceiverComponent;
+    // Primary -> managed intent: set unknown sources restriction and install package
+    public static final String ACTION_INSTALL_APK = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK";
+    public static final String EXTRA_ALLOW_NON_MARKET_APPS = INSTALL_NON_MARKET_APPS;
 
+    private static final int REQUEST_INSTALL_PACKAGE = 1;
+
+    private static final String ORIGINAL_SETTINGS_NAME = "original settings";
+    private Bundle mOriginalSettings;
+
+    private ComponentName mAdminReceiverComponent;
     private DevicePolicyManager mDevicePolicyManager;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        if (savedInstanceState != null) {
+            Log.w(TAG, "Restored state");
+            mOriginalSettings = savedInstanceState.getBundle(ORIGINAL_SETTINGS_NAME);
+        } else {
+            mOriginalSettings = new Bundle();
+        }
+
         mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
         mDevicePolicyManager = (DevicePolicyManager) getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
-        String action = getIntent().getAction();
+        Intent intent = getIntent();
+        String action = intent.getAction();
         Log.d(TAG, "ByodHelperActivity.onCreate: " + action);
 
         // we are explicitly started by {@link DeviceAdminTestReceiver} after a successful provisioning.
@@ -83,16 +103,70 @@
                 mDevicePolicyManager.wipeData(0);
                 showToast(R.string.provisioning_byod_profile_deleted);
             }
+        } else if (action.equals(ACTION_INSTALL_APK)) {
+            boolean allowNonMarket = intent.getBooleanExtra(EXTRA_ALLOW_NON_MARKET_APPS, false);
+            boolean wasAllowed = getAllowNonMarket();
+
+            // Update permission to install non-market apps
+            setAllowNonMarket(allowNonMarket);
+            mOriginalSettings.putBoolean(INSTALL_NON_MARKET_APPS, wasAllowed);
+
+            // Request to install a non-market application- easiest way is to reinstall ourself
+            final Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE)
+                    .setData(Uri.parse("package:" + getPackageName()))
+                    .putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
+                    .putExtra(Intent.EXTRA_RETURN_RESULT, true);
+            startActivityForResult(installIntent, REQUEST_INSTALL_PACKAGE);
+
+            // Not yet ready to finish- wait until the result comes back
+            return;
         }
         // This activity has no UI and is only used to respond to CtsVerifier in the primary side.
         finish();
     }
 
+    @Override
+    protected void onSaveInstanceState(final Bundle savedState) {
+        super.onSaveInstanceState(savedState);
+
+        savedState.putBundle(ORIGINAL_SETTINGS_NAME, mOriginalSettings);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case REQUEST_INSTALL_PACKAGE: {
+                Log.w(TAG, "Received REQUEST_INSTALL_PACKAGE, resultCode = " + resultCode);
+                if (mOriginalSettings.containsKey(INSTALL_NON_MARKET_APPS)) {
+                    // Restore original setting
+                    setAllowNonMarket(mOriginalSettings.getBoolean(INSTALL_NON_MARKET_APPS));
+                    mOriginalSettings.remove(INSTALL_NON_MARKET_APPS);
+                }
+                finish();
+                break;
+            }
+            default: {
+                Log.wtf(TAG, "Unknown requestCode " + requestCode + "; data = " + data);
+                break;
+            }
+        }
+    }
+
     private boolean isProfileOwner() {
         return mDevicePolicyManager.isAdminActive(mAdminReceiverComponent) &&
                 mDevicePolicyManager.isProfileOwnerApp(mAdminReceiverComponent.getPackageName());
     }
 
+    private boolean getAllowNonMarket() {
+        String value = Settings.Secure.getString(getContentResolver(), INSTALL_NON_MARKET_APPS);
+        return "1".equals(value);
+    }
+
+    private void setAllowNonMarket(boolean allow) {
+        mDevicePolicyManager.setSecureSetting(mAdminReceiverComponent, INSTALL_NON_MARKET_APPS,
+                (allow ? "1" : "0"));
+    }
+
     private void startActivityInPrimary(Intent intent) {
         // Disable app components in the current profile, so only the counterpart in the other
         // profile can respond (via cross-profile intent filter)
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 8dccac3..fa7bc4c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -26,8 +26,6 @@
 import android.util.Log;
 import android.widget.Toast;
 
-import com.android.cts.verifier.managedprovisioning.ByodHelperActivity;
-
 /**
  * Profile owner receiver for BYOD flow test.
  * Setup cross-profile intent filter after successful provisioning.
@@ -50,7 +48,10 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
             filter.addAction(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
+            filter.addAction(ByodHelperActivity.ACTION_INSTALL_APK);
             filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+            filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
+            filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
             dpm.addCrossProfileIntentFilter(getWho(context), filter,
                     DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java
new file mode 100644
index 0000000..c85ccf5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Test activity used to generate a notification.
+ */
+public class WorkNotificationTestActivity extends Activity {
+    public static final String ACTION_WORK_NOTIFICATION =
+            "com.android.cts.verifier.managedprovisioning.WORK_NOTIFICATION";
+    public static final String ACTION_CLEAR_WORK_NOTIFICATION =
+            "com.android.cts.verifier.managedprovisioning.CLEAR_WORK_NOTIFICATION";
+    private static final int NOTIFICATION_ID = 7;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final String action = getIntent().getAction();
+        final NotificationManager notificationManager = (NotificationManager)
+                getSystemService(Context.NOTIFICATION_SERVICE);
+        if (ACTION_WORK_NOTIFICATION.equals(action)) {
+            final Notification notification = new Notification.Builder(this)
+                .setSmallIcon(R.drawable.icon)
+                .setContentTitle(getString(R.string.provisioning_byod_work_notification_title))
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setAutoCancel(true)
+                .build();
+            notificationManager.notify(NOTIFICATION_ID, notification);
+        } else if (ACTION_CLEAR_WORK_NOTIFICATION.equals(action)) {
+            notificationManager.cancel(NOTIFICATION_ID);
+        }
+        finish();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
index 8aa82b5..5f3a10a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
@@ -45,7 +45,8 @@
             PrefixPaymentService2.COMPONENT,
             PrefixTransportService1.COMPONENT,
             PrefixTransportService2.COMPONENT,
-            PrefixAccessService.COMPONENT)
+            PrefixAccessService.COMPONENT,
+            LargeNumAidsService.COMPONENT)
     );
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
index 87fa3d1..b30bb73 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
@@ -104,6 +104,10 @@
                     new Intent(this, OnAndOffHostEmulatorActivity.class), null));
 
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                adapter.add(TestListItem.newTest(this, R.string.nfc_hce_large_num_aids_emulator,
+                        LargeNumAidsEmulatorActivity.class.getName(),
+                        new Intent(this, LargeNumAidsEmulatorActivity.class), null));
+
                 adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_dynamic_aids_emulator,
                         DynamicAidEmulatorActivity.class.getName(),
                         new Intent(this, DynamicAidEmulatorActivity.class), null));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
index 4c77871..035ce86 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
@@ -103,6 +103,10 @@
                     SimpleReaderActivity.class.getName(),
                     DynamicAidEmulatorActivity.buildReaderIntent(this), null));
 
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_large_num_aids_reader,
+                    SimpleReaderActivity.class.getName(),
+                    LargeNumAidsEmulatorActivity.buildReaderIntent(this), null));
+
             NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
             CardEmulation cardEmulation = CardEmulation.getInstance(nfcAdapter);
             if (cardEmulation.supportsAidPrefixRegistration()) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
index c67169a..698948b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
@@ -22,6 +22,9 @@
     public static final String TRANSPORT_PREFIX_AID = "F001020304";
     public static final String ACCESS_PREFIX_AID = "F005060708";
 
+    public static final String LARGE_NUM_AIDS_PREFIX = "F00102030414";
+    public static final String LARGE_NUM_AIDS_POSTFIX ="81";
+
     public static void enableComponent(PackageManager pm, ComponentName component) {
         pm.setComponentEnabledSetting(
                 component,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java
new file mode 100644
index 0000000..db58252
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java
@@ -0,0 +1,59 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+import java.util.ArrayList;
+
+public class LargeNumAidsEmulatorActivity extends BaseEmulatorActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+       super.onCreate(savedInstanceState);
+       setContentView(R.layout.pass_fail_text);
+       setPassFailButtonClickListeners();
+       getPassButton().setEnabled(false);
+       setupServices(this, LargeNumAidsService.COMPONENT);
+    }
+
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+
+    @Override
+    void onServicesSetup(boolean result) {
+        ArrayList<String> aids = new ArrayList<String>();
+        for (int i = 0; i < 256; i++) {
+            aids.add(HceUtils.LARGE_NUM_AIDS_PREFIX + String.format("%02X", i) + HceUtils.LARGE_NUM_AIDS_POSTFIX);
+        }
+        mCardEmulation.registerAidsForService(LargeNumAidsService.COMPONENT,
+                CardEmulation.CATEGORY_OTHER, aids);
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(LargeNumAidsService.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                LargeNumAidsService.getCommandSequence());
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                LargeNumAidsService.getResponseSequence());
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_large_num_aids_reader));
+        return readerIntent;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java
new file mode 100644
index 0000000..5883543
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java
@@ -0,0 +1,37 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class LargeNumAidsService extends HceService {
+    static final String TAG = "LargeNumAidsService";
+
+    static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+            LargeNumAidsService.class.getName());
+
+    public static final CommandApdu[] getCommandSequence() {
+        CommandApdu[] commands = new CommandApdu[256];
+        for (int i = 0; i < 256; i++) {
+            commands[i] = HceUtils.buildSelectApdu(HceUtils.LARGE_NUM_AIDS_PREFIX + String.format("%02X", i) +
+                    HceUtils.LARGE_NUM_AIDS_POSTFIX, true);
+        }
+        return commands;
+    }
+
+    public static final String[] getResponseSequence() {
+        String[] responses = new String[256];
+        for (int i = 0; i < 256; i++) {
+            responses[i] = "9000" + String.format("%02X", i);
+        }
+        return responses;
+    }
+
+    public LargeNumAidsService() {
+        initialize(getCommandSequence(), getResponseSequence());
+    }
+
+    @Override
+    public ComponentName getComponent() {
+        return COMPONENT;
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
new file mode 100644
index 0000000..200865e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -0,0 +1,242 @@
+/*
+ * 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.screenpinning;
+
+import android.app.ActivityManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+public class ScreenPinningTestActivity extends PassFailButtons.Activity {
+
+    private static final String TAG = "ScreenPinningTestActivity";
+    private static final String KEY_CURRENT_TEST = "keyCurrentTest";
+
+    private Test[] mTests;
+    private int mTestIndex;
+
+    private ActivityManager mActivityManager;
+    private Button mNextButton;
+    private LinearLayout mInstructions;
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.screen_pinning);
+        setPassFailButtonClickListeners();
+
+        mTests = new Test[] {
+            // Verify not already pinned.
+            mCheckStartedUnpinned,
+            // Enter pinning, verify pinned, try leaving and have the user exit.
+            mCheckStartPinning,
+            mCheckIsPinned,
+            mCheckTryLeave,
+            mCheckUnpin,
+            // Enter pinning, verify pinned, and use APIs to exit.
+            mCheckStartPinning,
+            mCheckIsPinned,
+            mCheckUnpinFromCode,
+            // All done.
+            mDone,
+        };
+
+        mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
+        mInstructions = (LinearLayout) findViewById(R.id.instructions_list);
+
+        mNextButton = (Button) findViewById(R.id.next_button);
+        mNextButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if ((mTestIndex >= 0) && (mTestIndex < mTests.length)) {
+                    mTests[mTestIndex].onNextClick();
+                }
+            }
+        });
+
+        // Don't allow pass until all tests complete.
+        findViewById(R.id.pass_button).setVisibility(View.GONE);
+
+        // Figure out if we are in a test or starting from the beginning.
+        if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_TEST)) {
+            mTestIndex = savedInstanceState.getInt(KEY_CURRENT_TEST);
+        } else {
+            mTestIndex = 0;
+        }
+        // Display any pre-existing text.
+        for (int i = 0; i < mTestIndex; i++) {
+            mTests[i].showText();
+        }
+        mTests[mTestIndex].run();
+    };
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putInt(KEY_CURRENT_TEST, mTestIndex);
+    }
+
+    @Override
+    public void onBackPressed() {
+        // Block back button so we can test screen pinning exit functionality.
+        // Users can still leave by pressing fail (or when done the pass) button.
+    }
+
+    private void show(int id) {
+        TextView tv = new TextView(this);
+        tv.setPadding(10, 10, 10, 30);
+        tv.setText(id);
+        mInstructions.addView(tv);
+    }
+
+    private void succeed() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mTestIndex++;
+                if (mTestIndex < mTests.length) {
+                    mTests[mTestIndex].run();
+                } else {
+                    mNextButton.setVisibility(View.GONE);
+                    findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
+                }
+            }
+        });
+    }
+
+    private void error(int errorId) {
+        error(errorId, new Throwable());
+    }
+
+    private void error(final int errorId, final Throwable cause) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                String error = getString(errorId);
+                Log.d(TAG, error, cause);
+                // No more instructions needed.
+                findViewById(R.id.instructions_group).setVisibility(View.GONE);
+
+                ((TextView) findViewById(R.id.error_text)).setText(error);
+            }
+        });
+    }
+
+    // Verify we don't start in screen pinning.
+    private final Test mCheckStartedUnpinned = new Test(0) {
+        public void run() {
+            if (mActivityManager.isInLockTaskMode()) {
+                error(R.string.error_screen_already_pinned);
+            } else {
+                succeed();
+            }
+        }
+    };
+
+    // Start screen pinning by having the user click next then confirm it for us.
+    private final Test mCheckStartPinning = new Test(R.string.screen_pin_instructions) {
+        protected void onNextClick() {
+            startLockTask();
+            succeed();
+        }
+    };
+
+    // Click next and check that we got pinned.
+    // Wait for the user to click next to verify that they got back from the prompt
+    // successfully.
+    private final Test mCheckIsPinned = new Test(R.string.screen_pin_check_pinned) {
+        protected void onNextClick() {
+            if (mActivityManager.isInLockTaskMode()) {
+                succeed();
+            } else {
+                error(R.string.error_screen_pinning_did_not_start);
+            }
+        }
+    };
+
+    // Tell user to try to leave.
+    private final Test mCheckTryLeave = new Test(R.string.screen_pin_no_exit) {
+        protected void onNextClick() {
+            if (mActivityManager.isInLockTaskMode()) {
+                succeed();
+            } else {
+                error(R.string.error_screen_no_longer_pinned);
+            }
+        }
+    };
+
+    // Verify that the user unpinned and it worked.
+    private final Test mCheckUnpin = new Test(R.string.screen_pin_exit) {
+        protected void onNextClick() {
+            if (!mActivityManager.isInLockTaskMode()) {
+                succeed();
+            } else {
+                error(R.string.error_screen_pinning_did_not_exit);
+            }
+        }
+    };
+
+    // Unpin from code and check that it worked.
+    private final Test mCheckUnpinFromCode = new Test(0) {
+        protected void run() {
+            if (!mActivityManager.isInLockTaskMode()) {
+                error(R.string.error_screen_pinning_did_not_start);
+                return;
+            }
+            stopLockTask();
+            if (!mActivityManager.isInLockTaskMode()) {
+                succeed();
+            } else {
+                error(R.string.error_screen_pinning_couldnt_exit);
+            }
+        };
+    };
+
+    private final Test mDone = new Test(R.string.screen_pinning_done) {
+        protected void run() {
+            showText();
+            succeed();
+        };
+    };
+
+    private abstract class Test {
+        private final int mResId;
+
+        public Test(int showId) {
+            mResId = showId;
+        }
+
+        protected void run() {
+            showText();
+        }
+
+        public void showText() {
+            if (mResId == 0) {
+                return;
+            }
+            show(mResId);
+        }
+
+        protected void onNextClick() {
+        }
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index 4a0c135..52b3dee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -101,7 +101,7 @@
         verifyMeasurements.addVerification(new MeanVerification(
                 expectations,
                 new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
-        verifyMeasurements.execute();
+        verifyMeasurements.execute(getCurrentTestNode());
         return null;
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
index 2d58c64..7ef63d7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -150,8 +150,7 @@
 
     private String executeTest(TestSensorOperation operation) throws InterruptedException {
         operation.addDefaultVerifications();
-        operation.setLogEvents(true);
-        operation.execute();
+        operation.execute(getCurrentTestNode());
         return null;
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index d6ec951..7be0fb1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -167,7 +167,7 @@
         sensorOperation.addVerification(integrationVerification);
 
         try {
-            sensorOperation.execute();
+            sensorOperation.execute(getCurrentTestNode());
         } finally {
             playSound();
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index 22ff1e5..229a9dc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -87,7 +87,7 @@
         verifyNorm.addVerification(new MagnitudeVerification(
                 expectedMagneticFieldEarth,
                 magneticFieldEarthThreshold));
-        verifyNorm.execute();
+        verifyNorm.execute(getCurrentTestNode());
         return null;
     }
 
@@ -127,7 +127,7 @@
 
         verifyStdDev.addVerification(new StandardDeviationVerification(
                 new float[]{2f, 2f, 2f} /* uT */));
-        verifyStdDev.execute();
+        verifyStdDev.execute(getCurrentTestNode());
         return null;
     }
 
@@ -169,6 +169,7 @@
                 getApplicationContext(),
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_NORMAL);
+
         TestSensorEventListener listener = new TestSensorEventListener(environment) {
             @Override
             public void onSensorChanged(SensorEvent event) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
index 5bbaaf7..380b282 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
@@ -26,6 +26,8 @@
 
 import android.content.Context;
 import android.hardware.cts.SensorTestCase;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import java.util.Enumeration;
 
@@ -138,10 +140,24 @@
             SensorTestCase sensorTestCase = (SensorTestCase) testCase;
             sensorTestCase.setContext(mContext);
             sensorTestCase.setEmulateSensorUnderLoad(false);
+            sensorTestCase.setCurrentTestNode(new TestNode(testCase));
             // TODO: set delayed assertion provider
         } else {
             throw new IllegalStateException("TestCase must be an instance of SensorTestCase.");
         }
         super.run(testCase);
     }
+
+    private class TestNode implements ISensorTestNode {
+        private final TestCase mTestCase;
+
+        public TestNode(TestCase testCase) {
+            mTestCase = testCase;
+        }
+
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return mTestCase.getClass().getSimpleName() + "_" + mTestCase.getName();
+        }
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
index a88abd0..8cf287a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
@@ -19,6 +19,8 @@
 
 import com.android.cts.verifier.sensors.reporting.SensorTestDetails;
 
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -35,6 +37,7 @@
     private volatile int mTestPassedCounter;
     private volatile int mTestSkippedCounter;
     private volatile int mTestFailedCounter;
+    private volatile ISensorTestNode mCurrentTestNode;
 
     /**
      * {@inheritDoc}
@@ -63,8 +66,12 @@
                 mTestFailedCounter);
     }
 
+    protected ISensorTestNode getCurrentTestNode() {
+        return mCurrentTestNode;
+    }
+
     private List<Method> findTestMethods() {
-        ArrayList<Method> testMethods = new ArrayList<Method>();
+        ArrayList<Method> testMethods = new ArrayList<>();
         for (Method method : mTestClass.getDeclaredMethods()) {
             if (Modifier.isPublic(method.getModifiers())
                     && method.getParameterTypes().length == 0
@@ -79,6 +86,7 @@
     private SensorTestDetails executeTest(Method testMethod) throws InterruptedException {
         String testMethodName = testMethod.getName();
         String testName = String.format("%s#%s", getTestClassName(), testMethodName);
+        mCurrentTestNode = new TestNode(testMethod);
 
         SensorTestDetails testDetails;
         try {
@@ -112,4 +120,17 @@
 
         return testDetails;
     }
+
+    private class TestNode implements ISensorTestNode {
+        private final Method mTestMethod;
+
+        public TestNode(Method testMethod) {
+            mTestMethod = testMethod;
+        }
+
+        @Override
+        public String getName() {
+            return mTestClass.getSimpleName() + "_" + mTestMethod.getName();
+        }
+    }
 }
diff --git a/build/test_executable.mk b/build/test_executable.mk
index e0352ba..02b3e4c 100644
--- a/build/test_executable.mk
+++ b/build/test_executable.mk
@@ -43,3 +43,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_executable_xml)
diff --git a/build/test_gtest_package.mk b/build/test_gtest_package.mk
index 6868081..dd1269b 100644
--- a/build/test_gtest_package.mk
+++ b/build/test_gtest_package.mk
@@ -52,3 +52,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_package_apk) $(cts_package_xml)
diff --git a/build/test_host_java_library.mk b/build/test_host_java_library.mk
index 8e071e4..8ed5670 100644
--- a/build/test_host_java_library.mk
+++ b/build/test_host_java_library.mk
@@ -42,3 +42,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_xml)
diff --git a/build/test_package.mk b/build/test_package.mk
index 72972b2..7589787 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -64,3 +64,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_package_apk) $(cts_package_xml)
diff --git a/build/test_target_java_library.mk b/build/test_target_java_library.mk
index c0d7a2a..04fffb9 100644
--- a/build/test_target_java_library.mk
+++ b/build/test_target_java_library.mk
@@ -44,3 +44,6 @@
 						-a $(CTS_TARGET_ARCH) \
 						-x "runtimeArgs->$(PRIVATE_RUNTIME_ARGS)" \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
index 085d672..cad6e4f 100644
--- a/build/test_uiautomator.mk
+++ b/build/test_uiautomator.mk
@@ -55,3 +55,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
diff --git a/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java b/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
index ceb9072..9f69242 100644
--- a/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
+++ b/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
@@ -38,6 +38,7 @@
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.IOException;
+import java.util.TimeZone;
 
 import javax.imageio.ImageIO;
 
@@ -289,6 +290,13 @@
             stream.write(testString.getBytes());
             stream.close();
 
+            // adjust 1st file's last-modified timestamp according to persist.sys.timezone
+            String deviceTimezone = mTestDevice.getProperty("persist.sys.timezone");
+            if (deviceTimezone != null) {
+                TimeZone tz = TimeZone.getTimeZone(deviceTimezone);
+                tmpFile.setLastModified(tmpFile.lastModified() + tz.getRawOffset());
+            }
+
             assertTrue(mTestDevice.syncFiles(tmpDir, externalStorePath));
             String tmpFileContents = mTestDevice.executeShellCommand(String.format("cat %s",
                     expectedDeviceFilePath));
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
index caa3e46..90cbed9 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
@@ -57,6 +57,9 @@
     private static final String APK_mips64 = "CtsSplitApp_mips64.apk";
     private static final String APK_mips = "CtsSplitApp_mips.apk";
 
+    private static final String APK_DIFF_REVISION = "CtsSplitAppDiffRevision.apk";
+    private static final String APK_DIFF_REVISION_v7 = "CtsSplitAppDiffRevision_v7.apk";
+
     private static final String APK_DIFF_VERSION = "CtsSplitAppDiffVersion.apk";
     private static final String APK_DIFF_VERSION_v7 = "CtsSplitAppDiffVersion_v7.apk";
 
@@ -233,6 +236,30 @@
         new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
     }
 
+    public void testDiffRevision() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+    }
+
+    public void testDiffRevisionInheritBase() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+    }
+
+    public void testDiffRevisionInheritSplit() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision12_0");
+    }
+
+    public void testDiffRevisionDowngrade() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_v7).runExpectingFailure();
+    }
+
     public void testFeatureBase() throws Exception {
         new InstallMultiple().addApk(APK).addApk(APK_FEATURE).run();
         runDeviceTests(PKG, ".SplitAppTest", "testFeatureBase");
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
index 8b25f4b..bf89576 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -30,7 +30,31 @@
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+
+#################################################
+# Define a variant with a different revision code
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSplitAppDiffRevision
+LOCAL_PACKAGE_SPLITS := v7
+
+LOCAL_MANIFEST_FILE := revision/AndroidManifest.xml
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundredRevisionTwelve --replace-version
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
@@ -53,7 +77,7 @@
 LOCAL_PACKAGE_SPLITS := v7
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
@@ -76,7 +100,7 @@
 LOCAL_PACKAGE_SPLITS := v7
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
index e93f6c3..809a6b8 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
 
 LOCAL_MODULE_TAGS := tests
 
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
new file mode 100644
index 0000000..8e053ba
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        android:revisionCode="12">
+
+    <uses-permission android:name="android.permission.CAMERA" />
+
+    <application android:label="SplitApp">
+        <activity android:name=".MyActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.service.wallpaper" android:resource="@xml/my_activity_meta" />
+        </activity>
+        <receiver android:name=".MyReceiver"
+                android:enabled="@bool/my_receiver_enabled">
+            <intent-filter>
+                <action android:name="android.intent.action.DATE_CHANGED" />
+            </intent-filter>
+        </receiver>
+
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.splitapp" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index 5046458..3d6cee7 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -21,6 +21,7 @@
 
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
@@ -30,6 +31,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
 import android.util.DisplayMetrics;
 import android.util.Log;
 
@@ -319,6 +321,30 @@
         assertFalse(new File(getContext().getCodeCacheDir(), "cache.raw").exists());
     }
 
+    public void testRevision0_0() throws Exception {
+        final PackageInfo info = getContext().getPackageManager()
+                .getPackageInfo(getContext().getPackageName(), 0);
+        assertEquals(0, info.baseRevisionCode);
+        assertEquals(1, info.splitRevisionCodes.length);
+        assertEquals(0, info.splitRevisionCodes[0]);
+    }
+
+    public void testRevision12_0() throws Exception {
+        final PackageInfo info = getContext().getPackageManager()
+                .getPackageInfo(getContext().getPackageName(), 0);
+        assertEquals(12, info.baseRevisionCode);
+        assertEquals(1, info.splitRevisionCodes.length);
+        assertEquals(0, info.splitRevisionCodes[0]);
+    }
+
+    public void testRevision0_12() throws Exception {
+        final PackageInfo info = getContext().getPackageManager()
+                .getPackageInfo(getContext().getPackageName(), 0);
+        assertEquals(0, info.baseRevisionCode);
+        assertEquals(1, info.splitRevisionCodes.length);
+        assertEquals(12, info.splitRevisionCodes[0]);
+    }
+
     private static void updateDpi(Resources r, int densityDpi) {
         final Configuration c = new Configuration(r.getConfiguration());
         c.densityDpi = densityDpi;
diff --git a/hostsidetests/theme/assets/21/ldpi.zip b/hostsidetests/theme/assets/21/ldpi.zip
index 059bf33..e33f2f0 100644
--- a/hostsidetests/theme/assets/21/ldpi.zip
+++ b/hostsidetests/theme/assets/21/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/21/mdpi.zip b/hostsidetests/theme/assets/21/mdpi.zip
index 0fb0778..f74739e 100644
--- a/hostsidetests/theme/assets/21/mdpi.zip
+++ b/hostsidetests/theme/assets/21/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
index 33e67e5..ba880d7 100644
--- a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
+++ b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
@@ -37,9 +37,7 @@
 
     private static final int IMAGE_THRESHOLD = 2;
 
-    private static final String STORAGE_PATH_DEVICE = "/storage/emulated/legacy/cts-holo-assets/%s.png";
-
-    private static final String STORAGE_PATH_EMULATOR = "/sdcard/cts-holo-assets/%s.png";
+    private static final String STORAGE_PATH_DEVICE = "/sdcard/cts-holo-assets/%s.png";
 
     private final ITestDevice mDevice;
 
@@ -47,18 +45,10 @@
 
     private final String mName;
 
-    private final String mStoragePath;
-
     public ComparisonTask(ITestDevice device, File reference, String name) {
         mDevice = device;
         mReference = reference;
         mName = name;
-
-        if (mDevice.getSerialNumber().startsWith("emulator-")) {
-            mStoragePath = STORAGE_PATH_EMULATOR;
-        } else {
-            mStoragePath = STORAGE_PATH_DEVICE;
-        }
     }
 
     public Boolean call() {
@@ -67,7 +57,7 @@
         try {
             generated = File.createTempFile("gen_" + mName, ".png");
 
-            final String remoteGenerated = String.format(mStoragePath, mName);
+            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;
@@ -84,7 +74,7 @@
                 Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath());
             }
         } catch (Exception e) {
-            Log.logAndDisplay(LogLevel.ERROR, TAG, String.format(mStoragePath, mName));
+            Log.logAndDisplay(LogLevel.ERROR, TAG, String.format(STORAGE_PATH_DEVICE, mName));
             Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
             e.printStackTrace();
         } finally {
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index a0177e2..38a753d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -34,7 +34,7 @@
     private static final String TAG = "MockJobService";
 
     /** Wait this long before timing out the test. */
-    private static final long DEFAULT_TIMEOUT_MILLIS = 5000L; // 5 seconds.
+    private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
 
     @Override
     public void onCreate() {
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
index 36f44ef..ed9cadd 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
@@ -29,7 +29,7 @@
 
     public void testScheduleOnce() throws Exception {
         JobInfo oneTimeJob = new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
-                        .setOverrideDeadline(1000)  // 1 secs
+                        .setOverrideDeadline(5000)  // 5 secs
                         .build();
 
         kTestEnvironment.setExpectedExecutions(1);
@@ -41,7 +41,7 @@
     public void testSchedulePeriodic() throws Exception {
         JobInfo periodicJob =
                 new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
-                        .setPeriodic(1000L)  // 1 second period.
+                        .setPeriodic(5000L)  // 5 second period.
                         .build();
 
         kTestEnvironment.setExpectedExecutions(3);
@@ -52,7 +52,8 @@
 
     public void testCancel() throws Exception {
         JobInfo cancelJob = new JobInfo.Builder(CANCEL_JOB_ID, kJobServiceComponent)
-                .setOverrideDeadline(2000L)
+                .setMinimumLatency(5000L) // make sure it doesn't actually run immediately
+                .setOverrideDeadline(7000L)
                 .build();
 
         kTestEnvironment.setExpectedExecutions(0);
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 7532fc8..c71eed2 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -131,37 +131,6 @@
   bug: 17508787
 },
 {
-  description: "New tests recently added for Android Enterprise. To be moved out of CTS-staging as soon as they show that they are stable",
-  names: [
-    "com.android.cts.devicepolicy.DeviceOwnerTest#testApplicationRestrictions",
-    "com.android.cts.devicepolicy.DeviceOwnerTest#testCaCertManagement",
-    "com.android.cts.devicepolicy.DeviceOwnerTest#testDeviceOwnerSetup",
-    "com.android.cts.devicepolicy.DeviceOwnerTest#testPersistentIntentResolving",
-    "com.android.cts.devicepolicy.DeviceOwnerTest#testScreenCaptureDisabled",
-    "com.android.cts.devicepolicy.ManagedProfileTest#testManagedProfileSetup",
-    "com.android.cts.devicepolicy.ManagedProfileTest#testWipeData",
-    "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileIntentFilters",
-    "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileContent",
-    "com.android.cts.devicepolicy.ManagedProfileTest#testNoDebuggingFeaturesRestriction",
-    "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileCopyPaste",
-    "com.android.cts.devicepolicy.DeviceOwnerTest#testLockTask"
-  ]
-},
-{
-
-  description: "These tests fail on some devices.",
-  names: [
-    "android.uirendering.cts.testclasses.ExactCanvasTests#testBlueRect",
-    "android.uirendering.cts.testclasses.ExactCanvasTests#testBluePaddedSquare",
-    "android.uirendering.cts.testclasses.ViewClippingTests#testSimplePaddingClip",
-    "android.uirendering.cts.testclasses.ViewClippingTests#testSimpleClipBoundsClip",
-    "android.uirendering.cts.testclasses.ViewClippingTests#testSimpleOutlineClip",
-    "android.uirendering.cts.testclasses.ViewClippingTests#testSimpleBoundsClip",
-    "android.uirendering.cts.testclasses.InfrastructureTests#testViewInitializer"
-  ],
-  bug: 17511118
-},
-{
   description: "This test failed on devices that use effect off loading. In addition it uses hidden apis",
   names: [
     "android.media.cts.AudioEffectTest#test1_1ConstructorFromUuid"
diff --git a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
index bfc3e1d..b85c616 100644
--- a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -18,14 +18,17 @@
 
 
 import android.app.AlarmManager;
+import android.app.AlarmManager.AlarmClockInfo;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.cts.util.PollingCheck;
+import android.os.Build;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.util.Log;
+import android.test.MoreAsserts;
 
 public class AlarmManagerTest extends AndroidTestCase {
     public static final String MOCKACTION = "android.app.AlarmManagerTest.TEST_ALARMRECEIVER";
@@ -47,6 +50,7 @@
 
     private static final int TIME_DELTA = 1000;
     private static final int TIME_DELAY = 10000;
+    private static final int REPEAT_PERIOD = 60000;
 
     // Receiver registration/unregistration between tests races with the system process, so
     // we add a little buffer time here to allow the system to process before we proceed.
@@ -221,46 +225,54 @@
 
     public void testSetRepeating() throws Exception {
         mMockAlarmReceiver.setAlarmedFalse();
-        mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
-        mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, TIME_DELAY / 2, mSender);
-        new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
+        mWakeupTime = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+        mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, REPEAT_PERIOD, mSender);
+
+        // wait beyond the initial alarm's possible delivery window to verify that it fires the first time
+        new PollingCheck(TEST_ALARM_FUTURITY + REPEAT_PERIOD) {
             @Override
             protected boolean check() {
                 return mMockAlarmReceiver.alarmed;
             }
         }.run();
+        assertTrue(mMockAlarmReceiver.alarmed);
+
+        // Now reset the receiver and wait for the intended repeat alarm to fire as expected
         mMockAlarmReceiver.setAlarmedFalse();
-        new PollingCheck(TIME_DELAY) {
+        new PollingCheck(REPEAT_PERIOD*2) {
             @Override
             protected boolean check() {
                 return mMockAlarmReceiver.alarmed;
             }
         }.run();
+        assertTrue(mMockAlarmReceiver.alarmed);
+
         mAm.cancel(mSender);
     }
 
     public void testCancel() throws Exception {
         mMockAlarmReceiver.setAlarmedFalse();
-        mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
-        mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, 1000, mSender);
-        new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
-            @Override
-            protected boolean check() {
-                return mMockAlarmReceiver.alarmed;
-            }
-        }.run();
-        mMockAlarmReceiver.setAlarmedFalse();
+        mMockAlarmReceiver2.setAlarmedFalse();
+
+        // set two alarms
+        final long when1 = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+        mAm.setExact(AlarmManager.RTC_WAKEUP, when1, mSender);
+        final long when2 = when1 + TIME_DELTA; // will fire after when1's target time
+        mAm.setExact(AlarmManager.RTC_WAKEUP, when2, mSender2);
+
+        // cancel the earlier one
+        mAm.cancel(mSender);
+
+        // and verify that only the later one fired
         new PollingCheck(TIME_DELAY) {
             @Override
             protected boolean check() {
-                return mMockAlarmReceiver.alarmed;
+                return mMockAlarmReceiver2.alarmed;
             }
         }.run();
-        mAm.cancel(mSender);
-        Thread.sleep(TIME_DELAY);
-        mMockAlarmReceiver.setAlarmedFalse();
-        Thread.sleep(TIME_DELAY * 5);
+
         assertFalse(mMockAlarmReceiver.alarmed);
+        assertTrue(mMockAlarmReceiver2.alarmed);
     }
 
     public void testSetInexactRepeating() throws Exception {
@@ -273,4 +285,66 @@
         // " Unable to open alarm driver: Permission denied". But still fail
         // after tried many permission.
     }
+
+    public void testSetAlarmClock() throws Exception {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            mMockAlarmReceiver.setAlarmedFalse();
+            mMockAlarmReceiver2.setAlarmedFalse();
+
+            // Set first alarm clock.
+            final long wakeupTimeFirst = System.currentTimeMillis()
+                    + 2 * TEST_ALARM_FUTURITY;
+            mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeFirst, null), mSender);
+
+            // Verify getNextAlarmClock returns first alarm clock.
+            AlarmClockInfo nextAlarmClock = mAm.getNextAlarmClock();
+            assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+            assertNull(nextAlarmClock.getShowIntent());
+
+            // Set second alarm clock, earlier than first.
+            final long wakeupTimeSecond = System.currentTimeMillis()
+                    + TEST_ALARM_FUTURITY;
+            PendingIntent showIntentSecond = PendingIntent.getBroadcast(getContext(), 0,
+                    new Intent(getContext(), AlarmManagerTest.class).setAction("SHOW_INTENT"), 0);
+            mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeSecond, showIntentSecond),
+                    mSender2);
+
+            // Verify getNextAlarmClock returns second alarm clock now.
+            nextAlarmClock = mAm.getNextAlarmClock();
+            assertEquals(wakeupTimeSecond, nextAlarmClock.getTriggerTime());
+            assertEquals(showIntentSecond, nextAlarmClock.getShowIntent());
+
+            // Cancel second alarm.
+            mAm.cancel(mSender2);
+
+            // Verify getNextAlarmClock returns first alarm clock again.
+            nextAlarmClock = mAm.getNextAlarmClock();
+            assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+            assertNull(nextAlarmClock.getShowIntent());
+
+            // Wait for first alarm to trigger.
+            assertFalse(mMockAlarmReceiver.alarmed);
+            new PollingCheck(2 * TEST_ALARM_FUTURITY + TIME_DELAY) {
+                @Override
+                protected boolean check() {
+                    return mMockAlarmReceiver.alarmed;
+                }
+            }.run();
+
+            // Verify first alarm fired at the right time.
+            assertEquals(mMockAlarmReceiver.rtcTime, wakeupTimeFirst, TIME_DELTA);
+
+            // Verify second alarm didn't fire.
+            assertFalse(mMockAlarmReceiver2.alarmed);
+
+            // Verify the next alarm is not returning neither the first nor the second alarm.
+            nextAlarmClock = mAm.getNextAlarmClock();
+            MoreAsserts.assertNotEqual(wakeupTimeFirst, nextAlarmClock != null
+                    ? nextAlarmClock.getTriggerTime()
+                    : null);
+            MoreAsserts.assertNotEqual(wakeupTimeSecond, nextAlarmClock != null
+                    ? nextAlarmClock.getTriggerTime()
+                    : null);
+        }
+    }
 }
diff --git a/tests/tests/app/src/android/app/cts/DialogTest.java b/tests/tests/app/src/android/app/cts/DialogTest.java
index 6df2eee..feb4940 100644
--- a/tests/tests/app/src/android/app/cts/DialogTest.java
+++ b/tests/tests/app/src/android/app/cts/DialogTest.java
@@ -673,19 +673,36 @@
         mInstrumentation.waitForIdleSync();
     }
 
-    public void testSetFeatureDrawableUri() {
+    public void testSetFeatureDrawableUri() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
-        mActivity.getDialog().setFeatureDrawableUri(0, Uri.parse("http://www.google.com"));
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getDialog().setFeatureDrawableUri(Window.FEATURE_LEFT_ICON,
+                        Uri.parse("http://www.google.com"));
+            }
+        });
+        mInstrumentation.waitForIdleSync();
     }
 
-    public void testSetFeatureDrawable() {
+    public void testSetFeatureDrawable() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
-        mActivity.getDialog().setFeatureDrawable(0, new MockDrawable());
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getDialog().setFeatureDrawable(Window.FEATURE_LEFT_ICON, 
+                        new MockDrawable());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
     }
 
-    public void testSetFeatureDrawableAlpha() {
+    public void testSetFeatureDrawableAlpha() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
-        mActivity.getDialog().setFeatureDrawableAlpha(0, 0);
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getDialog().setFeatureDrawableAlpha(Window.FEATURE_LEFT_ICON, 0);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
     }
 
     public void testGetLayoutInflater() {
diff --git a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
index 84faffa..a68d860 100644
--- a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -49,7 +49,7 @@
     private static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
 
     private static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
-    private static final long LONG_TIMEOUT = 2 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
 
     private DownloadManager mDownloadManager;
 
diff --git a/tests/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
index 0c2e9fa..b21148e 100644
--- a/tests/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -196,10 +196,12 @@
 
     public void testInvokeMenuActionSync() throws Exception {
         final int resId = R.id.goto_menu_id;
-        mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
-        mInstrumentation.waitForIdleSync();
-
-        assertEquals(resId, mActivity.getMenuID());
+        if (mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+            mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
+            mInstrumentation.waitForIdleSync();
+    
+            assertEquals(resId, mActivity.getMenuID());
+        }
     }
 
     public void testCallActivityOnPostCreate() throws Throwable {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index dd17779..872f951 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -447,22 +447,26 @@
             assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
             for (int row = 0; row < h; row++) {
                 int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+                int length;
                 if (pixelStride == bytesPerPixel) {
                     // Special case: optimized read of the entire row
-                    int length = w * bytesPerPixel;
+                    length = w * bytesPerPixel;
                     buffer.get(data, offset, length);
-                    // Advance buffer the remainder of the row stride
-                    buffer.position(buffer.position() + rowStride - length);
                     offset += length;
                 } else {
                     // Generic case: should work for any pixelStride but slower.
                     // Use intermediate buffer to avoid read byte-by-byte from
                     // DirectByteBuffer, which is very bad for performance
-                    buffer.get(rowData, 0, rowStride);
+                    length = (w - 1) * pixelStride + bytesPerPixel;
+                    buffer.get(rowData, 0, length);
                     for (int col = 0; col < w; col++) {
                         data[offset++] = rowData[col * pixelStride];
                     }
                 }
+                // Advance buffer the remainder of the row stride
+                if (row < h - 1) {
+                    buffer.position(buffer.position() + rowStride - length);
+                }
             }
             if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
             buffer.rewind();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
index 9ec649e..d5972e2 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -16,15 +16,22 @@
 
 package android.hardware.camera2.cts;
 
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
-import android.hardware.camera2.CameraCaptureSession;
+import android.graphics.RectF;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.DngCreator;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
+import android.hardware.camera2.cts.rs.RawConverter;
+import android.hardware.camera2.cts.rs.RenderScriptSingleton;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.location.Location;
 import android.media.ExifInterface;
@@ -37,11 +44,14 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
+import java.nio.channels.FileChannel;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
-import static android.hardware.camera2.cts.CameraTestUtils.configureCameraSession;
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+import static junit.framework.Assert.assertTrue;
 
 /**
  * Tests for the DngCreator API.
@@ -51,6 +61,9 @@
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final String DEBUG_DNG_FILE = "raw16.dng";
 
+    private static final double IMAGE_DIFFERENCE_TOLERANCE = 60;
+    private static final int DEFAULT_PATCH_DIMEN = 512;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -61,6 +74,13 @@
         super.tearDown();
     }
 
+    @Override
+    public synchronized void setContext(Context context) {
+        super.setContext(context);
+
+        RenderScriptSingleton.setContext(context);
+    }
+
     /**
      * Test basic raw capture and DNG saving functionality for each of the available cameras.
      *
@@ -120,14 +140,15 @@
                 dngCreator.writeImage(outputStream, resultPair.first);
 
                 if (VERBOSE) {
-                    String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+                    // Write DNG to file
+                    String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_basic_" + deviceId + "_" +
                             DEBUG_DNG_FILE;
                     // Write out captured DNG file for the first camera device if setprop is enabled
-                    fileStream = new FileOutputStream(filePath);
+                    fileStream = new FileOutputStream(dngFilePath);
                     fileStream.write(outputStream.toByteArray());
                     fileStream.flush();
                     fileStream.close();
-                    Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + filePath);
+                    Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
                 }
             } finally {
                 closeDevice(deviceId);
@@ -231,7 +252,7 @@
                 dngCreator.writeImage(outputStream, resultPair.first.get(0));
 
                 if (VERBOSE) {
-                    String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+                    String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
                             DEBUG_DNG_FILE;
                     // Write out captured DNG file for the first camera device if setprop is enabled
                     fileStream = new FileOutputStream(filePath);
@@ -257,6 +278,222 @@
         }
     }
 
+    /**
+     * Test basic RAW capture, and ensure that the rendered RAW output is similar to the JPEG
+     * created for the same frame.
+     *
+     * <p>
+     * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline
+     * similar to one in the Adobe DNG validation tool.  JPEGs produced by the vendor hardware may
+     * have different tonemapping and saturation applied than the RGB bitmaps produced
+     * from this DNG rendering pipeline, and this test allows for fairly wide variations
+     * between the histograms for the RAW and JPEG buffers to avoid false positives.
+     * </p>
+     *
+     * <p>
+     * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW
+     * metadata, the DNGs and JPEGs produced here should also be manually compared using external
+     * DNG rendering tools.  The DNG, rendered RGB bitmap, and JPEG buffer for this test can be
+     * dumped to the SD card for further examination by enabling the 'verbose' mode for this test
+     * using:
+     * adb shell setprop log.tag.DngCreatorTest VERBOSE
+     * </p>
+     */
+    public void testRaw16JpegConsistency() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            String deviceId = mCameraIds[i];
+            List<ImageReader> captureReaders = new ArrayList<ImageReader>();
+            List<CameraTestUtils.SimpleImageReaderListener> captureListeners =
+                    new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
+            FileOutputStream fileStream = null;
+            ByteArrayOutputStream outputStream = null;
+            FileChannel fileChannel = null;
+            try {
+                openDevice(deviceId);
+
+                if (!mStaticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                            ". Skip the test.");
+                    continue;
+                }
+
+                Size[] targetCaptureSizes =
+                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+                                StaticMetadata.StreamDirection.Output);
+
+                assertTrue("No capture sizes available for RAW format!",
+                        targetCaptureSizes.length != 0);
+                Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
+                Size activeArraySize = new Size(activeArray.width(), activeArray.height());
+                assertTrue("Active array has invalid size!", activeArray.width() > 0 &&
+                        activeArray.height() > 0);
+                // TODO: Allow PixelArraySize also.
+                assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
+                        targetCaptureSizes, activeArraySize);
+
+                // Get largest jpeg size
+                Size[] targetJpegSizes =
+                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+                                StaticMetadata.StreamDirection.Output);
+
+                Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes),
+                        new CameraTestUtils.SizeComparator());
+
+                // Create raw image reader and capture listener
+                CameraTestUtils.SimpleImageReaderListener rawListener
+                        = new CameraTestUtils.SimpleImageReaderListener();
+                captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
+                        rawListener));
+                captureListeners.add(rawListener);
+
+
+                // Create jpeg image reader and capture listener
+                CameraTestUtils.SimpleImageReaderListener jpegListener
+                        = new CameraTestUtils.SimpleImageReaderListener();
+                captureReaders.add(createImageReader(largestJpegSize, ImageFormat.JPEG, 2,
+                        jpegListener));
+                captureListeners.add(jpegListener);
+
+                Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
+                        captureReaders, captureListeners);
+                CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
+                Image raw = resultPair.first.get(0);
+                Image jpeg = resultPair.first.get(1);
+
+                Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(),
+                        Bitmap.Config.ARGB_8888);
+                byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
+
+                // Render RAW image to a bitmap
+                raw.getPlanes()[0].getBuffer().get(rawPlane);
+                raw.getPlanes()[0].getBuffer().rewind();
+                RawConverter.convertToSRGB(RenderScriptSingleton.getRS(), raw.getWidth(),
+                        raw.getHeight(), rawPlane, characteristics,
+                        resultPair.second, /*offsetX*/0, /*offsetY*/0, /*out*/rawBitmap);
+
+                // Decompress JPEG image to a bitmap
+                byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg);
+
+                BitmapFactory.Options opt = new BitmapFactory.Options();
+                opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                Bitmap fullSizeJpegBmap = BitmapFactory.decodeByteArray(compressedJpegData,
+                        /*offset*/0, compressedJpegData.length, /*inout*/opt);
+                Rect jpegDimens = new Rect(0, 0, fullSizeJpegBmap.getWidth(),
+                        fullSizeJpegBmap.getHeight());
+
+                if (VERBOSE) {
+                    // Generate DNG file
+                    DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
+                    outputStream = new ByteArrayOutputStream();
+                    dngCreator.writeImage(outputStream, raw);
+
+                    // Write DNG to file
+                    String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_" +
+                            DEBUG_DNG_FILE;
+                    // Write out captured DNG file for the first camera device if setprop is enabled
+                    fileStream = new FileOutputStream(dngFilePath);
+                    fileStream.write(outputStream.toByteArray());
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
+
+                    // Write JPEG to file
+                    String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_jpeg.jpg";
+                    // Write out captured DNG file for the first camera device if setprop is enabled
+                    fileChannel = new FileOutputStream(jpegFilePath).getChannel();
+                    fileChannel.write(jpeg.getPlanes()[0].getBuffer());
+                    fileChannel.close();
+                    Log.v(TAG, "Test JPEG file for camera " + deviceId + " saved to " +
+                            jpegFilePath);
+
+                    // Write jpeg generated from demosaiced RAW frame to file
+                    String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_raw.jpg";
+                    // Write out captured DNG file for the first camera device if setprop is enabled
+                    fileStream = new FileOutputStream(rawFilePath);
+                    rawBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.v(TAG, "Test converted RAW file for camera " + deviceId + " saved to " +
+                            rawFilePath);
+                }
+
+                Size rawBitmapSize = new Size(rawBitmap.getWidth(), rawBitmap.getHeight());
+                assertTrue("Raw bitmap size must be equal to active array size.",
+                        rawBitmapSize.equals(activeArraySize));
+
+                // Get square center patch from JPEG and RAW bitmaps
+                RectF jpegRect = new RectF(jpegDimens);
+                RectF rawRect = new RectF(0, 0, rawBitmap.getWidth(), rawBitmap.getHeight());
+                int sideDimen = Math.min(Math.min(Math.min(Math.min(DEFAULT_PATCH_DIMEN,
+                        jpegDimens.width()), jpegDimens.height()), rawBitmap.getWidth()),
+                        rawBitmap.getHeight());
+
+                RectF jpegIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+                jpegIntermediate.offset(jpegRect.centerX() - jpegIntermediate.centerX(),
+                        jpegRect.centerY() - jpegIntermediate.centerY());
+                RectF rawIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+                rawIntermediate.offset(rawRect.centerX() - rawIntermediate.centerX(),
+                        rawRect.centerY() - rawIntermediate.centerY());
+                Rect jpegFinal = new Rect();
+                jpegIntermediate.roundOut(jpegFinal);
+                Rect rawFinal = new Rect();
+                rawIntermediate.roundOut(rawFinal);
+
+                Bitmap jpegPatch = Bitmap.createBitmap(fullSizeJpegBmap, jpegFinal.left,
+                        jpegFinal.top, jpegFinal.width(), jpegFinal.height());
+                Bitmap rawPatch = Bitmap.createBitmap(rawBitmap, rawFinal.left, rawFinal.top,
+                        rawFinal.width(), rawFinal.height());
+
+                // Compare center patch from JPEG and rendered RAW bitmap
+                double difference = BitmapUtils.calcDifferenceMetric(jpegPatch, rawPatch);
+                if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
+                    // Write JPEG patch to file
+                    String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+                            "_jpeg_patch.jpg";
+                    fileStream = new FileOutputStream(jpegFilePath);
+                    jpegPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.e(TAG, "Failed JPEG patch file for camera " + deviceId + " saved to " +
+                            jpegFilePath);
+
+                    // Write RAW patch to file
+                    String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+                            "_raw_patch.jpg";
+                    fileStream = new FileOutputStream(rawFilePath);
+                    rawPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.e(TAG, "Failed RAW patch file for camera " + deviceId + " saved to " +
+                            rawFilePath);
+
+                    fail("Camera " + mCamera.getId() + ": RAW and JPEG image at  for the same " +
+                            "frame are not similar, center patches have difference metric of " +
+                            difference);
+                }
+
+            } finally {
+                closeDevice(deviceId);
+                for (ImageReader r : captureReaders) {
+                    closeImageReader(r);
+                }
+
+                if (fileChannel != null) {
+                    fileChannel.close();
+                }
+
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+
+                if (fileStream != null) {
+                    fileStream.close();
+                }
+            }
+        }
+    }
+
     private Pair<Image, CaptureResult> captureSingleRawShot(Size s, ImageReader captureReader,
             CameraTestUtils.SimpleImageReaderListener captureListener) throws Exception {
         List<ImageReader> readers = new ArrayList<ImageReader>();
@@ -268,22 +505,27 @@
         return new Pair<Image, CaptureResult>(res.first.get(0), res.second);
     }
 
+    private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s, List<ImageReader> captureReaders,
+            List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+        return captureRawShots(s, captureReaders, captureListeners, 1).get(0);
+    }
+
     /**
-     * Capture a single raw image.
+     * Capture raw images.
      *
-     * <p>Capture an raw image for a given size.</p>
+     * <p>Capture raw images for a given size.</p>
      *
      * @param s The size of the raw image to capture.  Must be one of the available sizes for this
      *          device.
-     * @return a pair containing the {@link Image} and {@link CaptureResult} used for this capture.
+     * @return a list of pairs containing a {@link Image} and {@link CaptureResult} used for
+     *          each capture.
      */
-    private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s, List<ImageReader> captureReaders,
-            List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+    private List<Pair<List<Image>, CaptureResult>> captureRawShots(Size s, List<ImageReader> captureReaders,
+            List<CameraTestUtils.SimpleImageReaderListener> captureListeners, int numShots) throws Exception {
         if (VERBOSE) {
             Log.v(TAG, "captureSingleRawShot - Capturing raw image.");
         }
 
-        Size maxYuvSz = mOrderedPreviewSizes.get(0);
         Size[] targetCaptureSizes =
                 mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
                         StaticMetadata.StreamDirection.Output);
@@ -312,23 +554,29 @@
         CameraTestUtils.SimpleCaptureCallback resultListener =
                 new CameraTestUtils.SimpleCaptureCallback();
 
-        startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
+        CaptureRequest request1 = request.build();
+        for (int i = 0; i < numShots; i++) {
+            startCapture(request1, /*repeating*/false, resultListener, mHandler);
+        }
+        List<Pair<List<Image>, CaptureResult>> ret = new ArrayList<>();
+        for (int i = 0; i < numShots; i++) {
+            // Verify capture result and images
+            CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
 
-        // Verify capture result and images
-        CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
-
-        List<Image> resultImages = new ArrayList<Image>();
-        for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
-            Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+            List<Image> resultImages = new ArrayList<Image>();
+            for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
+                Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
 
             /*CameraTestUtils.validateImage(captureImage, s.getWidth(), s.getHeight(),
                     ImageFormat.RAW_SENSOR, null);*/
-            resultImages.add(captureImage);
+                resultImages.add(captureImage);
+            }
+            ret.add(new Pair<List<Image>, CaptureResult>(resultImages, result));
         }
         // Stop capture, delete the streams.
         stopCapture(/*fast*/false);
 
-        return new Pair<List<Image>, CaptureResult>(resultImages, result);
+        return ret;
     }
 
     private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
@@ -336,7 +584,7 @@
         createSession(surfaces);
 
         CaptureRequest.Builder captureBuilder =
-                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
         assertNotNull("Fail to get captureRequest", captureBuilder);
         for (Surface surface : surfaces) {
             captureBuilder.addTarget(surface);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 9dd5ed8..250601d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraCharacteristics.Key;
 import android.hardware.camera2.CameraManager;
@@ -25,11 +26,13 @@
 import android.hardware.camera2.params.BlackLevelPattern;
 import android.hardware.camera2.params.ColorSpaceTransform;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.ImageReader;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.Rational;
 import android.util.Range;
 import android.util.Size;
+import android.view.Surface;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -437,6 +440,179 @@
     }
 
     /**
+     * Cross-check StreamConfigurationMap output
+     */
+    public void testStreamConfigurationMap() {
+        int counter = 0;
+        for (CameraCharacteristics c : mCharacteristics) {
+            Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
+            StreamConfigurationMap config =
+                    c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            assertNotNull(String.format("No stream configuration map found for: ID %s",
+                            mIds[counter]), config);
+
+            assertTrue("ImageReader must be supported",
+                    config.isOutputSupportedFor(android.media.ImageReader.class));
+            assertTrue("MediaRecorder must be supported",
+                    config.isOutputSupportedFor(android.media.MediaRecorder.class));
+            assertTrue("MediaCodec must be supported",
+                    config.isOutputSupportedFor(android.media.MediaCodec.class));
+            assertTrue("Allocation must be supported",
+                    config.isOutputSupportedFor(android.renderscript.Allocation.class));
+            assertTrue("SurfaceHolder must be supported",
+                    config.isOutputSupportedFor(android.view.SurfaceHolder.class));
+            assertTrue("SurfaceTexture must be supported",
+                    config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
+
+            assertTrue("YUV_420_888 must be supported",
+                    config.isOutputSupportedFor(ImageFormat.YUV_420_888));
+            assertTrue("JPEG must be supported",
+                    config.isOutputSupportedFor(ImageFormat.JPEG));
+
+            // Legacy YUV formats should not be listed
+            assertTrue("NV21 must not be supported",
+                    !config.isOutputSupportedFor(ImageFormat.NV21));
+
+            int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+            assertNotNull("android.request.availableCapabilities must never be null",
+                    actualCapabilities);
+            if (arrayContains(actualCapabilities,
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
+                    config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
+            }
+
+            // Cross check public formats and sizes
+
+            int[] supportedFormats = config.getOutputFormats();
+            for (int format : supportedFormats) {
+                assertTrue("Format " + format + " fails cross check",
+                        config.isOutputSupportedFor(format));
+                Size[] supportedSizes = config.getOutputSizes(format);
+                assertTrue("Supported format " + format + " has no sizes listed",
+                        supportedSizes.length > 0);
+                for (Size size : supportedSizes) {
+                    if (VERBOSE) {
+                        Log.v(TAG,
+                                String.format("Testing camera %s, format %d, size %s",
+                                        mIds[counter], format, size.toString()));
+                    }
+
+                    long stallDuration = config.getOutputStallDuration(format, size);
+                    switch(format) {
+                        case ImageFormat.YUV_420_888:
+                            assertTrue("YUV_420_888 may not have a non-zero stall duration",
+                                    stallDuration == 0);
+                            break;
+                        default:
+                            assertTrue("Negative stall duration for format " + format,
+                                    stallDuration >= 0);
+                            break;
+                    }
+                    long minDuration = config.getOutputMinFrameDuration(format, size);
+                    if (arrayContains(actualCapabilities,
+                            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                        assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+                                + "format " + format,
+                                minDuration > 0);
+                    } else {
+                        assertTrue("Need non-negative min frame duration for format " + format,
+                                minDuration >= 0);
+                    }
+
+                    ImageReader testReader = ImageReader.newInstance(
+                        size.getWidth(),
+                        size.getHeight(),
+                        format,
+                        1);
+                    Surface testSurface = testReader.getSurface();
+
+                    assertTrue(
+                        String.format("isOutputSupportedFor fails for config %s, format %d",
+                                size.toString(), format),
+                        config.isOutputSupportedFor(testSurface));
+
+                    testReader.close();
+
+                } // sizes
+
+                // Try an invalid size in this format, should round
+                Size invalidSize = findInvalidSize(supportedSizes);
+                int MAX_ROUNDING_WIDTH = 1920;
+                if (invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
+                    ImageReader testReader = ImageReader.newInstance(
+                                                                     invalidSize.getWidth(),
+                                                                     invalidSize.getHeight(),
+                                                                     format,
+                                                                     1);
+                    Surface testSurface = testReader.getSurface();
+
+                    assertTrue(
+                               String.format("isOutputSupportedFor fails for config %s, %d",
+                                       invalidSize.toString(), format),
+                               config.isOutputSupportedFor(testSurface));
+
+                    testReader.close();
+                }
+            } // formats
+
+            // Cross-check opaque format and sizes
+
+            SurfaceTexture st = new SurfaceTexture(1);
+            Surface surf = new Surface(st);
+
+            Size[] opaqueSizes = config.getOutputSizes(SurfaceTexture.class);
+            assertTrue("Opaque format has no sizes listed",
+                    opaqueSizes.length > 0);
+            for (Size size : opaqueSizes) {
+                long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
+                assertTrue("Opaque output may not have a non-zero stall duration",
+                        stallDuration == 0);
+
+                long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
+                if (arrayContains(actualCapabilities,
+                                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                    assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+                            + "opaque format",
+                            minDuration > 0);
+                } else {
+                    assertTrue("Need non-negative min frame duration for opaque format ",
+                            minDuration >= 0);
+                }
+                st.setDefaultBufferSize(size.getWidth(), size.getHeight());
+
+                assertTrue(
+                    String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+                            size.toString()),
+                    config.isOutputSupportedFor(surf));
+
+            } // opaque sizes
+
+            // Try invalid opaque size, should get rounded
+            Size invalidSize = findInvalidSize(opaqueSizes);
+            st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
+            assertTrue(
+                String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+                        invalidSize.toString()),
+                config.isOutputSupportedFor(surf));
+
+            counter++;
+        } // mCharacteristics
+
+    }
+
+    /**
+     * Create an invalid size that's close to one of the good sizes in the list, but not one of them
+     */
+    private Size findInvalidSize(Size[] goodSizes) {
+        Size invalidSize = new Size(goodSizes[0].getWidth() + 1, goodSizes[0].getHeight());
+        while(arrayContains(goodSizes, invalidSize)) {
+            invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
+        }
+        return invalidSize;
+    }
+
+    /**
      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
      * check that the key is present if the actual capabilities are one of {@code capabilities}.
      *
@@ -509,6 +685,20 @@
         return false;
     }
 
+    private static <T> boolean arrayContains(T[] arr, T needle) {
+        if (arr == null) {
+            return false;
+        }
+
+        for (T elem : arr) {
+            if (elem.equals(needle)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
         for (int needle : needles) {
             if (arrayContains(arr, needle)) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index 9089a8c..a410775 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -30,6 +30,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
@@ -364,7 +365,7 @@
                                     yuvPatch.width(), yuvPatch.height(), /*filter*/true);
 
                             // Compare two patches using average of per-pixel differences
-                            double difference = findDifferenceMetric(yuvBmap, jpegBmap);
+                            double difference = BitmapUtils.calcDifferenceMetric(yuvBmap, jpegBmap);
 
                             Log.i(TAG, "Difference for resolution " + captureSz + " is: " +
                                     difference);
@@ -413,39 +414,6 @@
     }
 
     /**
-     * Find the difference between two bitmaps using average of per-pixel differences.
-     *
-     * @param a first {@link Bitmap}.
-     * @param b second {@link Bitmap}.
-     * @return the difference.
-     */
-    private static double findDifferenceMetric(Bitmap a, Bitmap b) {
-        if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
-            throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
-                    a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
-                    b.getHeight());
-        }
-        // TODO: Optimize this in renderscript to avoid copy.
-        int[] aPixels = new int[a.getHeight() * a.getWidth()];
-        int[] bPixels = new int[aPixels.length];
-        a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
-                a.getHeight());
-        b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
-                b.getHeight());
-        double diff = 0;
-        for (int i = 0; i < aPixels.length; i++) {
-            int aPix = aPixels[i];
-            int bPix = bPixels[i];
-
-            diff += Math.abs(Color.red(aPix) - Color.red(bPix)); // red
-            diff += Math.abs(Color.green(aPix) - Color.green(bPix)); // green
-            diff += Math.abs(Color.blue(aPix) - Color.blue(bPix)); // blue
-        }
-        diff /= (aPixels.length * 3);
-        return diff;
-    }
-
-    /**
      * Convert a rectangular patch in a YUV image to an ARGB color array.
      *
      * @param w width of the patch.
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 397407b..0eb3404 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -369,7 +369,8 @@
         Log.i(TAG, String.format("Testing Camera %s, config %s",
                         cameraId, MaxOutputSizes.configToString(config)));
 
-        final int TIMEOUT_FOR_RESULT_MS = 1000;
+        // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
+        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
         final int MIN_RESULT_COUNT = 3;
 
         ImageDropperListener imageDropperListener = new ImageDropperListener();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
new file mode 100644
index 0000000..744d2c7
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicHistogram;
+
+/**
+ * Utility class providing methods for various pixel-wise ARGB bitmap operations.
+ */
+public class BitmapUtils {
+    private static final String TAG = "BitmapUtils";
+    private static final int COLOR_BIT_DEPTH = 256;
+
+    public static int A = 3;
+    public static int R = 0;
+    public static int G = 1;
+    public static int B = 2;
+    public static int NUM_CHANNELS = 4;
+
+    /**
+     * Return the histograms for each color channel (interleaved).
+     *
+     * @param rs a {@link RenderScript} context to use.
+     * @param bmap a {@link Bitmap} to generate the histograms for.
+     * @return an array containing NUM_CHANNELS * COLOR_BIT_DEPTH histogram bucket values, with
+     * the color channels interleaved.
+     */
+    public static int[] calcHistograms(RenderScript rs, Bitmap bmap) {
+        ScriptIntrinsicHistogram hist = ScriptIntrinsicHistogram.create(rs, Element.U8_4(rs));
+        Allocation sums = Allocation.createSized(rs, Element.I32_4(rs), COLOR_BIT_DEPTH);
+
+        // Setup input allocation (ARGB 8888 bitmap).
+        Allocation input = Allocation.createFromBitmap(rs, bmap);
+
+        hist.setOutput(sums);
+        hist.forEach(input);
+        int[] output = new int[COLOR_BIT_DEPTH * NUM_CHANNELS];
+        sums.copyTo(output);
+        return output;
+    }
+
+    /**
+     * Find the difference between two bitmaps using average of per-pixel differences.
+     *
+     * @param a first {@link android.graphics.Bitmap}.
+     * @param b second {@link android.graphics.Bitmap}.
+     * @return the difference.
+     */
+    public static double calcDifferenceMetric(Bitmap a, Bitmap b) {
+        if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
+            throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
+                    a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
+                    b.getHeight());
+        }
+        // TODO: Optimize this in renderscript to avoid copy.
+        int[] aPixels = new int[a.getHeight() * a.getWidth()];
+        int[] bPixels = new int[aPixels.length];
+        a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
+                a.getHeight());
+        b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
+                b.getHeight());
+        double diff = 0;
+        for (int i = 0; i < aPixels.length; i++) {
+            int aPix = aPixels[i];
+            int bPix = bPixels[i];
+
+            diff += Math.abs(Color.red(aPix) - Color.red(bPix)); // red
+            diff += Math.abs(Color.green(aPix) - Color.green(bPix)); // green
+            diff += Math.abs(Color.blue(aPix) - Color.blue(bPix)); // blue
+        }
+        diff /= (aPixels.length * 3);
+        return diff;
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
new file mode 100644
index 0000000..2cd2469
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.LensShadingMap;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+import android.renderscript.Int4;
+import android.renderscript.Matrix3f;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+
+import android.hardware.camera2.cts.ScriptC_raw_converter;
+import android.util.Log;
+import android.util.Rational;
+import android.util.SparseIntArray;
+
+import java.util.Arrays;
+
+/**
+ * Utility class providing methods for rendering RAW16 images into other colorspaces.
+ */
+public class RawConverter {
+    private static final String TAG = "RawConverter";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    /**
+     * Matrix to convert from CIE XYZ colorspace to sRGB, Bradford-adapted to D65.
+     */
+    private static final float[] sXYZtoRGBBradford = new float[] {
+            3.1338561f, -1.6168667f, -0.4906146f,
+            -0.9787684f, 1.9161415f, 0.0334540f,
+            0.0719453f, -0.2289914f, 1.4052427f
+    };
+
+    /**
+     * Matrix to convert from the ProPhoto RGB colorspace to CIE XYZ colorspace.
+     */
+    private static final float[] sProPhotoToXYZ = new float[] {
+            0.797779f, 0.135213f, 0.031303f,
+            0.288000f, 0.711900f, 0.000100f,
+            0.000000f, 0.000000f, 0.825105f
+    };
+
+    /**
+     * Matrix to convert from CIE XYZ colorspace to ProPhoto RGB colorspace.
+     */
+    private static final float[] sXYZtoProPhoto = new float[] {
+            1.345753f, -0.255603f, -0.051025f,
+            -0.544426f, 1.508096f, 0.020472f,
+            0.000000f, 0.000000f, 1.211968f
+    };
+
+    /**
+     * Coefficients for a 3rd order polynomial, ordered from highest to lowest power.  This
+     * polynomial approximates the default tonemapping curve used for ACR3.
+     */
+    private static final float[] DEFAULT_ACR3_TONEMAP_CURVE_COEFFS = new float[] {
+            1.041f, -2.973f, 2.932f, 0f
+    };
+
+    /**
+     * The D50 whitepoint coordinates in CIE XYZ colorspace.
+     */
+    private static final float[] D50_XYZ = new float[] { 0.9642f, 1, 0.8249f };
+
+    /**
+     * An array containing the color temperatures for standard reference illuminants.
+     */
+    private static final SparseIntArray sStandardIlluminants = new SparseIntArray();
+    private static final int NO_ILLUMINANT = -1;
+    static {
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT, 6504);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D65, 6504);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D50, 5003);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D55, 5503);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D75, 7504);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_A, 2856);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_B, 4874);
+        sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_C, 6774);
+        sStandardIlluminants.append(
+                CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT_FLUORESCENT, 6430);
+        sStandardIlluminants.append(
+                CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT, 4230);
+        sStandardIlluminants.append(
+                CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_WHITE_FLUORESCENT, 3450);
+        // TODO: Add the rest of the illuminants included in the LightSource EXIF tag.
+    }
+
+    /**
+     * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+     *
+     * <p> This function applies the operations roughly outlined in the Adobe DNG specification
+     * using the provided metadata about the image sensor.  Sensor data for Android devices is
+     * assumed to be relatively linear, and no extra linearization step is applied here.  The
+     * following operations are applied in the given order:</p>
+     *
+     * <ul>
+     *     <li>
+     *         Black level subtraction - the black levels given in the SENSOR_BLACK_LEVEL_PATTERN
+     *         tag are subtracted from the corresponding raw pixels.
+     *     </li>
+     *     <li>
+     *         Rescaling - each raw pixel is scaled by 1/(white level - black level).
+     *     </li>
+     *     <li>
+     *         Lens shading correction - the interpolated gains from the gain map defined in the
+     *         STATISTICS_LENS_SHADING_CORRECTION_MAP are applied to each raw pixel.
+     *     </li>
+     *     <li>
+     *         Clipping - each raw pixel is clipped to a range of [0.0, 1.0].
+     *     </li>
+     *     <li>
+     *         Demosaic - the RGB channels for each pixel are retrieved from the Bayer mosaic
+     *         of raw pixels using a simple bilinear-interpolation demosaicing algorithm.
+     *     </li>
+     *     <li>
+     *         Colorspace transform to wide-gamut RGB - each pixel is mapped into a
+     *         wide-gamut colorspace (in this case ProPhoto RGB is used) from the sensor
+     *         colorspace.
+     *     </li>
+     *     <li>
+     *         Tonemapping - A basic tonemapping curve using the default from ACR3 is applied
+     *         (no further exposure compensation is applied here, though this could be improved).
+     *     </li>
+     *     <li>
+     *         Colorspace transform to final RGB - each pixel is mapped into linear sRGB colorspace.
+     *     </li>
+     *     <li>
+     *         Gamma correction - each pixel is gamma corrected using γ=2.2 to map into sRGB
+     *         colorspace for viewing.
+     *     </li>
+     *     <li>
+     *         Packing - each pixel is scaled so that each color channel has a range of [0, 255],
+     *         and is packed into an Android bitmap.
+     *     </li>
+     * </ul>
+     *
+     * <p> Arguments given here are assumed to come from the values for the corresponding
+     * {@link CameraCharacteristics.Key}s defined for the camera that produced this RAW16 buffer.
+     * </p>
+     * @param rs a {@link RenderScript} context to use.
+     * @param inputWidth width of the input RAW16 image in pixels.
+     * @param inputHeight height of the input RAW16 image in pixels.
+     * @param rawImageInput a byte array containing a RAW16 image.
+     * @param staticMetadata the {@link CameraCharacteristics} for this RAW capture.
+     * @param dynamicMetadata the {@link CaptureResult} for this RAW capture.
+     * @param outputOffsetX the offset width into the raw image of the left side of the output
+     *                      rectangle.
+     * @param outputOffsetY the offset height into the raw image of the top side of the output
+     *                      rectangle.
+     * @param argbOutput a {@link Bitmap} to output the rendered RAW image into.  The height and
+     *                   width of this bitmap along with the output offsets are used to determine
+     *                   the dimensions and offset of the output rectangle contained in the RAW
+     *                   image to be rendered.
+     */
+    public static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight,
+            byte[] rawImageInput, CameraCharacteristics staticMetadata,
+            CaptureResult dynamicMetadata, int outputOffsetX, int outputOffsetY,
+            /*out*/Bitmap argbOutput) {
+        int cfa = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+        int[] blackLevelPattern = new int[4];
+        staticMetadata.get(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN).
+                copyTo(blackLevelPattern, /*offset*/0);
+        int whiteLevel = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
+        int ref1 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1);
+        int ref2 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2);
+        float[] calib1 = new float[9];
+        float[] calib2 = new float[9];
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1), calib1);
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2), calib2);
+        float[] color1 = new float[9];
+        float[] color2 = new float[9];
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1), color1);
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2), color2);
+        float[] forward1 = new float[9];
+        float[] forward2 = new float[9];
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1), forward1);
+        convertColorspaceTransform(
+                staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2), forward2);
+
+        Rational[] neutral = dynamicMetadata.get(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
+
+        LensShadingMap shadingMap = dynamicMetadata.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+
+        convertToSRGB(rs, inputWidth, inputHeight, cfa, blackLevelPattern, whiteLevel,
+                rawImageInput, ref1, ref2, calib1, calib2, color1, color2,
+                forward1, forward2, neutral, shadingMap, outputOffsetX, outputOffsetY, argbOutput);
+    }
+
+    /**
+     * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+     *
+     * @see #convertToSRGB
+     */
+    private static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight, int cfa,
+            int[] blackLevelPattern, int whiteLevel, byte[] rawImageInput,
+            int referenceIlluminant1, int referenceIlluminant2, float[] calibrationTransform1,
+            float[] calibrationTransform2, float[] colorMatrix1, float[] colorMatrix2,
+            float[] forwardTransform1, float[] forwardTransform2, Rational[/*3*/] neutralColorPoint,
+            LensShadingMap lensShadingMap, int outputOffsetX, int outputOffsetY,
+            /*out*/Bitmap argbOutput) {
+
+        // Validate arguments
+        if (argbOutput == null || rs == null || rawImageInput == null) {
+            throw new IllegalArgumentException("Null argument to convertToSRGB");
+        }
+        if (argbOutput.getConfig() != Bitmap.Config.ARGB_8888) {
+            throw new IllegalArgumentException(
+                    "Output bitmap passed to convertToSRGB is not ARGB_8888 format");
+        }
+        if (outputOffsetX < 0 || outputOffsetY < 0) {
+            throw new IllegalArgumentException("Negative offset passed to convertToSRGB");
+        }
+        int outWidth = argbOutput.getWidth();
+        int outHeight = argbOutput.getHeight();
+        if (outWidth + outputOffsetX > inputWidth || outHeight + outputOffsetY > inputHeight) {
+            throw new IllegalArgumentException("Raw image with dimensions (w=" + inputWidth +
+                    ", h=" + inputHeight + "), cannot converted into sRGB image with dimensions (w="
+                    + outWidth + ", h=" + outHeight + ").");
+        }
+        if (cfa < 0 || cfa > 3) {
+            throw new IllegalArgumentException("Unsupported cfa pattern " + cfa + " used.");
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Metadata Used:");
+            Log.d(TAG, "Input width,height: " + inputWidth + "," + inputHeight);
+            Log.d(TAG, "Output offset x,y: " + outputOffsetX + "," + outputOffsetY);
+            Log.d(TAG, "Output width,height: " + outWidth + "," + outHeight);
+            Log.d(TAG, "CFA: " + cfa);
+            Log.d(TAG, "BlackLevelPattern: " + Arrays.toString(blackLevelPattern));
+            Log.d(TAG, "WhiteLevel: " + whiteLevel);
+            Log.d(TAG, "ReferenceIlluminant1: " + referenceIlluminant1);
+            Log.d(TAG, "ReferenceIlluminant2: " + referenceIlluminant2);
+            Log.d(TAG, "CalibrationTransform1: " + Arrays.toString(calibrationTransform1));
+            Log.d(TAG, "CalibrationTransform2: " + Arrays.toString(calibrationTransform2));
+            Log.d(TAG, "ColorMatrix1: " + Arrays.toString(colorMatrix1));
+            Log.d(TAG, "ColorMatrix2: " + Arrays.toString(colorMatrix2));
+            Log.d(TAG, "ForwardTransform1: " + Arrays.toString(forwardTransform1));
+            Log.d(TAG, "ForwardTransform2: " + Arrays.toString(forwardTransform2));
+            Log.d(TAG, "NeutralColorPoint: " + Arrays.toString(neutralColorPoint));
+        }
+
+        Allocation gainMap = null;
+        if (lensShadingMap != null) {
+            float[] lsm = new float[lensShadingMap.getGainFactorCount()];
+            lensShadingMap.copyGainFactors(/*inout*/lsm, /*offset*/0);
+            gainMap = createFloat4Allocation(rs, lsm, lensShadingMap.getColumnCount(),
+                    lensShadingMap.getRowCount());
+        }
+
+        float[] normalizedForwardTransform1 = Arrays.copyOf(forwardTransform1,
+                forwardTransform1.length);
+        normalizeFM(normalizedForwardTransform1);
+        float[] normalizedForwardTransform2 = Arrays.copyOf(forwardTransform2,
+                forwardTransform2.length);
+        normalizeFM(normalizedForwardTransform2);
+
+        float[] normalizedColorMatrix1 = Arrays.copyOf(colorMatrix1, colorMatrix1.length);
+        normalizeCM(normalizedColorMatrix1);
+        float[] normalizedColorMatrix2 = Arrays.copyOf(colorMatrix2, colorMatrix2.length);
+        normalizeCM(normalizedColorMatrix2);
+
+        if (DEBUG) {
+            Log.d(TAG, "Normalized ForwardTransform1: " + Arrays.toString(normalizedForwardTransform1));
+            Log.d(TAG, "Normalized ForwardTransform2: " + Arrays.toString(normalizedForwardTransform2));
+            Log.d(TAG, "Normalized ColorMatrix1: " + Arrays.toString(normalizedColorMatrix1));
+            Log.d(TAG, "Normalized ColorMatrix2: " + Arrays.toString(normalizedColorMatrix2));
+        }
+
+        // Calculate full sensor colorspace to sRGB colorspace transform.
+        double interpolationFactor = findDngInterpolationFactor(referenceIlluminant1,
+                referenceIlluminant2, calibrationTransform1, calibrationTransform2,
+                normalizedColorMatrix1, normalizedColorMatrix2, neutralColorPoint);
+        if (DEBUG) Log.d(TAG, "Interpolation factor used: " + interpolationFactor);
+        float[] sensorToXYZ = new float[9];
+        calculateCameraToXYZD50Transform(normalizedForwardTransform1, normalizedForwardTransform2,
+                calibrationTransform1, calibrationTransform2, neutralColorPoint,
+                interpolationFactor, /*out*/sensorToXYZ);
+        if (DEBUG) Log.d(TAG, "CameraToXYZ xform used: " + Arrays.toString(sensorToXYZ));
+        float[] sensorToProPhoto = new float[9];
+        multiply(sXYZtoProPhoto, sensorToXYZ, /*out*/sensorToProPhoto);
+        if (DEBUG) Log.d(TAG, "CameraToIntemediate xform used: " + Arrays.toString(sensorToProPhoto));
+        Allocation output = Allocation.createFromBitmap(rs, argbOutput);
+
+        float[] proPhotoToSRGB = new float[9];
+        multiply(sXYZtoRGBBradford, sProPhotoToXYZ, /*out*/proPhotoToSRGB);
+
+        // Setup input allocation (16-bit raw pixels)
+        Type.Builder typeBuilder = new Type.Builder(rs, Element.U16(rs));
+        typeBuilder.setX(inputWidth);
+        typeBuilder.setY(inputHeight);
+        Type inputType = typeBuilder.create();
+        Allocation input = Allocation.createTyped(rs, inputType);
+        input.copyFromUnchecked(rawImageInput);
+
+        // Setup RS kernel globals
+        ScriptC_raw_converter converterKernel = new ScriptC_raw_converter(rs);
+        converterKernel.set_inputRawBuffer(input);
+        converterKernel.set_whiteLevel(whiteLevel);
+        converterKernel.set_sensorToIntermediate(new Matrix3f(transpose(sensorToProPhoto)));
+        converterKernel.set_intermediateToSRGB(new Matrix3f(transpose(proPhotoToSRGB)));
+        converterKernel.set_offsetX(outputOffsetX);
+        converterKernel.set_offsetY(outputOffsetY);
+        converterKernel.set_rawHeight(inputHeight);
+        converterKernel.set_rawWidth(inputWidth);
+        converterKernel.set_neutralPoint(new Float3(neutralColorPoint[0].floatValue(),
+                neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()));
+        converterKernel.set_toneMapCoeffs(new Float4(DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[0],
+                DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[1], DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[2],
+                DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[3]));
+        converterKernel.set_hasGainMap(gainMap != null);
+        if (gainMap != null) {
+            converterKernel.set_gainMap(gainMap);
+            converterKernel.set_gainMapWidth(lensShadingMap.getColumnCount());
+            converterKernel.set_gainMapHeight(lensShadingMap.getRowCount());
+        }
+
+        converterKernel.set_cfaPattern(cfa);
+        converterKernel.set_blackLevelPattern(new Int4(blackLevelPattern[0],
+                blackLevelPattern[1], blackLevelPattern[2], blackLevelPattern[3]));
+        converterKernel.forEach_convert_RAW_To_ARGB(output);
+        output.copyTo(argbOutput);  // Force RS sync with bitmap (does not do an extra copy).
+    }
+
+    /**
+     * Create a float-backed renderscript {@link Allocation} with the given dimensions, containing
+     * the contents of the given float array.
+     *
+     * @param rs a {@link RenderScript} context to use.
+     * @param fArray the float array to copy into the {@link Allocation}.
+     * @param width the width of the {@link Allocation}.
+     * @param height the height of the {@link Allocation}.
+     * @return an {@link Allocation} containing the given floats.
+     */
+    private static Allocation createFloat4Allocation(RenderScript rs, float[] fArray,
+                                                    int width, int height) {
+        if (fArray.length != width * height * 4) {
+            throw new IllegalArgumentException("Invalid float array of length " + fArray.length +
+                    ", must be correct size for Allocation of dimensions " + width + "x" + height);
+        }
+        Type.Builder builder = new Type.Builder(rs, Element.F32_4(rs));
+        builder.setX(width);
+        builder.setY(height);
+        Allocation fAlloc = Allocation.createTyped(rs, builder.create());
+        fAlloc.copyFrom(fArray);
+        return fAlloc;
+    }
+
+    /**
+     * Calculate the correlated color temperature (CCT) for a given x,y chromaticity in CIE 1931 x,y
+     * chromaticity space using McCamy's cubic approximation algorithm given in:
+     *
+     * McCamy, Calvin S. (April 1992).
+     * "Correlated color temperature as an explicit function of chromaticity coordinates".
+     * Color Research & Application 17 (2): 142–144
+     *
+     * @param x x chromaticity component.
+     * @param y y chromaticity component.
+     *
+     * @return the CCT associated with this chromaticity coordinate.
+     */
+    private static double calculateColorTemperature(double x, double y) {
+        double n = (x - 0.332) / (y - 0.1858);
+        return -449 * Math.pow(n, 3) + 3525 * Math.pow(n, 2) - 6823.3 * n + 5520.33;
+    }
+
+    /**
+     * Calculate the x,y chromaticity coordinates in CIE 1931 x,y chromaticity space from the given
+     * CIE XYZ coordinates.
+     *
+     * @param X the CIE XYZ X coordinate.
+     * @param Y the CIE XYZ Y coordinate.
+     * @param Z the CIE XYZ Z coordinate.
+     *
+     * @return the [x, y] chromaticity coordinates as doubles.
+     */
+    private static double[] calculateCIExyCoordinates(double X, double Y, double Z) {
+        double[] ret = new double[] { 0, 0 };
+        ret[0] = X / (X + Y + Z);
+        ret[1] = Y / (X + Y + Z);
+        return ret;
+    }
+
+    /**
+     * Linearly interpolate between a and b given fraction f.
+     *
+     * @param a first term to interpolate between, a will be returned when f == 0.
+     * @param b second term to interpolate between, b will be returned when f == 1.
+     * @param f the fraction to interpolate by.
+     *
+     * @return interpolated result as double.
+     */
+    private static double lerp(double a, double b, double f) {
+        return (a * (1.0f - f)) + (b * f);
+    }
+
+    /**
+     * Linearly interpolate between 3x3 matrices a and b given fraction f.
+     *
+     * @param a first 3x3 matrix to interpolate between, a will be returned when f == 0.
+     * @param b second 3x3 matrix to interpolate between, b will be returned when f == 1.
+     * @param f the fraction to interpolate by.
+     * @param result will be set to contain the interpolated matrix.
+     */
+    private static void lerp(float[] a, float[] b, double f, /*out*/float[] result) {
+        for (int i = 0; i < 9; i++) {
+            result[i] = (float) lerp(a[i], b[i], f);
+        }
+    }
+
+    /**
+     * Convert a 9x9 {@link ColorSpaceTransform} to a matrix and write the matrix into the
+     * output.
+     *
+     * @param xform a {@link ColorSpaceTransform} to transform.
+     * @param output the 3x3 matrix to overwrite.
+     */
+    private static void convertColorspaceTransform(ColorSpaceTransform xform, /*out*/float[] output) {
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                output[i * 3 + j] = xform.getElement(j, i).floatValue();
+            }
+        }
+    }
+
+    /**
+     * Find the interpolation factor to use with the RAW matrices given a neutral color point.
+     *
+     * @param referenceIlluminant1 first reference illuminant.
+     * @param referenceIlluminant2 second reference illuminant.
+     * @param calibrationTransform1 calibration matrix corresponding to the first reference
+     *                              illuminant.
+     * @param calibrationTransform2 calibration matrix corresponding to the second reference
+     *                              illuminant.
+     * @param colorMatrix1 color matrix corresponding to the first reference illuminant.
+     * @param colorMatrix2 color matrix corresponding to the second reference illuminant.
+     * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+     *
+     * @return the interpolation factor corresponding to the given neutral color point.
+     */
+    private static double findDngInterpolationFactor(int referenceIlluminant1,
+            int referenceIlluminant2, float[] calibrationTransform1, float[] calibrationTransform2,
+            float[] colorMatrix1, float[] colorMatrix2, Rational[/*3*/] neutralColorPoint) {
+
+        int colorTemperature1 = sStandardIlluminants.get(referenceIlluminant1, NO_ILLUMINANT);
+        if (colorTemperature1 == NO_ILLUMINANT) {
+            throw new IllegalArgumentException("No such illuminant for reference illuminant 1: " +
+                    referenceIlluminant1);
+        }
+
+        int colorTemperature2 = sStandardIlluminants.get(referenceIlluminant2, NO_ILLUMINANT);
+        if (colorTemperature2 == NO_ILLUMINANT) {
+            throw new IllegalArgumentException("No such illuminant for reference illuminant 2: " +
+                    referenceIlluminant2);
+        }
+
+        if (DEBUG) Log.d(TAG, "ColorTemperature1: " + colorTemperature1);
+        if (DEBUG) Log.d(TAG, "ColorTemperature2: " + colorTemperature2);
+
+        double interpFactor = 0.5; // Initial guess for interpolation factor
+        double oldInterpFactor = interpFactor;
+
+        double lastDiff = Double.MAX_VALUE;
+        double tolerance = 0.0001;
+        float[] XYZToCamera1 = new float[9];
+        float[] XYZToCamera2 = new float[9];
+        multiply(calibrationTransform1, colorMatrix1, /*out*/XYZToCamera1);
+        multiply(calibrationTransform2, colorMatrix2, /*out*/XYZToCamera2);
+
+        float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+                neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+
+        float[] neutralGuess = new float[3];
+        float[] interpXYZToCamera = new float[9];
+        float[] interpXYZToCameraInverse = new float[9];
+
+
+        double lower = Math.min(colorTemperature1, colorTemperature2);
+        double upper = Math.max(colorTemperature1, colorTemperature2);
+
+        if(DEBUG) {
+            Log.d(TAG, "XYZtoCamera1: " + Arrays.toString(XYZToCamera1));
+            Log.d(TAG, "XYZtoCamera2: " + Arrays.toString(XYZToCamera2));
+            Log.d(TAG, "Finding interpolation factor, initial guess 0.5...");
+        }
+        // Iteratively guess xy value, find new CCT, and update interpolation factor.
+        int loopLimit = 30;
+        int count = 0;
+        while (lastDiff > tolerance && loopLimit > 0) {
+            if (DEBUG) Log.d(TAG, "Loop count " + count);
+            lerp(XYZToCamera1, XYZToCamera2, interpFactor, interpXYZToCamera);
+            if (!invert(interpXYZToCamera, /*out*/interpXYZToCameraInverse)) {
+                throw new IllegalArgumentException(
+                        "Cannot invert XYZ to Camera matrix, input matrices are invalid.");
+            }
+
+            map(interpXYZToCameraInverse, cameraNeutral, /*out*/neutralGuess);
+            double[] xy = calculateCIExyCoordinates(neutralGuess[0], neutralGuess[1],
+                    neutralGuess[2]);
+
+            double colorTemperature = calculateColorTemperature(xy[0], xy[1]);
+
+            if (colorTemperature <= lower) {
+                interpFactor = 1;
+            } else if (colorTemperature >= upper) {
+                interpFactor = 0;
+            } else {
+                double invCT = 1.0 / colorTemperature;
+                interpFactor = (invCT - 1.0 / upper) / ( 1.0 / lower - 1.0 / upper);
+            }
+
+            if (lower == colorTemperature1) {
+                interpFactor = 1.0 - interpFactor;
+            }
+
+            interpFactor = (interpFactor + oldInterpFactor) / 2;
+            lastDiff = Math.abs(oldInterpFactor - interpFactor);
+            oldInterpFactor = interpFactor;
+            loopLimit--;
+            count++;
+
+            if (DEBUG) {
+                Log.d(TAG, "CameraToXYZ chosen: " + Arrays.toString(interpXYZToCameraInverse));
+                Log.d(TAG, "XYZ neutral color guess: " + Arrays.toString(neutralGuess));
+                Log.d(TAG, "xy coordinate: " + Arrays.toString(xy));
+                Log.d(TAG, "xy color temperature: " + colorTemperature);
+                Log.d(TAG, "New interpolation factor: " + interpFactor);
+            }
+        }
+
+        if (loopLimit == 0) {
+            Log.w(TAG, "Could not converge on interpolation factor, using factor " + interpFactor +
+                    " with remaining error factor of " + lastDiff);
+        }
+        return interpFactor;
+    }
+
+    /**
+     * Calculate the transform from the raw camera sensor colorspace to CIE XYZ colorspace with a
+     * D50 whitepoint.
+     *
+     * @param forwardTransform1 forward transform matrix corresponding to the first reference
+     *                          illuminant.
+     * @param forwardTransform2 forward transform matrix corresponding to the second reference
+     *                          illuminant.
+     * @param calibrationTransform1 calibration transform matrix corresponding to the first
+     *                              reference illuminant.
+     * @param calibrationTransform2 calibration transform matrix corresponding to the second
+     *                              reference illuminant.
+     * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+     * @param interpolationFactor the interpolation factor to use for the forward and
+     *                            calibration transforms.
+     * @param outputTransform set to the full sensor to XYZ colorspace transform.
+     */
+    private static void calculateCameraToXYZD50Transform(float[] forwardTransform1,
+            float[] forwardTransform2, float[] calibrationTransform1, float[] calibrationTransform2,
+            Rational[/*3*/] neutralColorPoint, double interpolationFactor,
+            /*out*/float[] outputTransform) {
+        float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+                neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+        if (DEBUG) Log.d(TAG, "Camera neutral: " + Arrays.toString(cameraNeutral));
+
+        float[] interpolatedCC = new float[9];
+        lerp(calibrationTransform1, calibrationTransform2, interpolationFactor,
+                interpolatedCC);
+        float[] inverseInterpolatedCC = new float[9];
+        if (!invert(interpolatedCC, /*out*/inverseInterpolatedCC)) {
+            throw new IllegalArgumentException( "Cannot invert interpolated calibration transform" +
+                    ", input matrices are invalid.");
+        }
+        if (DEBUG) Log.d(TAG, "Inverted interpolated CalibrationTransform: " +
+                Arrays.toString(inverseInterpolatedCC));
+
+        float[] referenceNeutral = new float[3];
+        map(inverseInterpolatedCC, cameraNeutral, /*out*/referenceNeutral);
+        if (DEBUG) Log.d(TAG, "Reference neutral: " + Arrays.toString(referenceNeutral));
+        float[] D = new float[] { 1/referenceNeutral[0], 0, 0,  0, 1/referenceNeutral[1], 0, 0, 0,
+                1/referenceNeutral[2] };
+        if (DEBUG) Log.d(TAG, "Reference Neutral Diagonal: " + Arrays.toString(D));
+
+        float[] intermediate = new float[9];
+        float[] intermediate2 = new float[9];
+
+        lerp(forwardTransform1, forwardTransform2, interpolationFactor, /*out*/intermediate);
+        if (DEBUG) Log.d(TAG, "Interpolated ForwardTransform: " + Arrays.toString(intermediate));
+
+        multiply(D, inverseInterpolatedCC, /*out*/intermediate2);
+        multiply(intermediate, intermediate2, /*out*/outputTransform);
+    }
+
+    /**
+     * Map a 3d column vector using the given matrix.
+     *
+     * @param matrix float array containing 3x3 matrix to map vector by.
+     * @param input 3 dimensional vector to map.
+     * @param output 3 dimensional vector result.
+     */
+    private static void map(float[] matrix, float[] input, /*out*/float[] output) {
+        output[0] = input[0] * matrix[0] + input[1] * matrix[1] + input[2] * matrix[2];
+        output[1] = input[0] * matrix[3] + input[1] * matrix[4] + input[2] * matrix[5];
+        output[2] = input[0] * matrix[6] + input[1] * matrix[7] + input[2] * matrix[8];
+    }
+
+    /**
+     * Multiply two 3x3 matrices together: A * B
+     *
+     * @param a left matrix.
+     * @param b right matrix.
+     */
+    private static void multiply(float[] a, float[] b, /*out*/float[] output) {
+        output[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
+        output[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
+        output[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
+        output[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
+        output[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
+        output[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
+        output[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
+        output[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
+        output[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
+    }
+
+    /**
+     * Transpose a 3x3 matrix in-place.
+     *
+     * @param m the matrix to transpose.
+     * @return the transposed matrix.
+     */
+    private static float[] transpose(/*inout*/float[/*9*/] m) {
+        float t = m[1];
+        m[1] = m[3];
+        m[3] = t;
+        t = m[2];
+        m[2] = m[6];
+        m[6] = t;
+        t = m[5];
+        m[5] = m[7];
+        m[7] = t;
+        return m;
+    }
+
+    /**
+     * Invert a 3x3 matrix, or return false if the matrix is singular.
+     *
+     * @param m matrix to invert.
+     * @param output set the output to be the inverse of m.
+     */
+    private static boolean invert(float[] m, /*out*/float[] output) {
+        double a00 = m[0];
+        double a01 = m[1];
+        double a02 = m[2];
+        double a10 = m[3];
+        double a11 = m[4];
+        double a12 = m[5];
+        double a20 = m[6];
+        double a21 = m[7];
+        double a22 = m[8];
+
+        double t00 = a11 * a22 - a21 * a12;
+        double t01 = a21 * a02 - a01 * a22;
+        double t02 = a01 * a12 - a11 * a02;
+        double t10 = a20 * a12 - a10 * a22;
+        double t11 = a00 * a22 - a20 * a02;
+        double t12 = a10 * a02 - a00 * a12;
+        double t20 = a10 * a21 - a20 * a11;
+        double t21 = a20 * a01 - a00 * a21;
+        double t22 = a00 * a11 - a10 * a01;
+
+        double det = a00 * t00 + a01 * t10 + a02 * t20;
+        if (Math.abs(det) < 1e-9) {
+            return false; // Inverse too close to zero, not invertible.
+        }
+
+        output[0] = (float) (t00 / det);
+        output[1] = (float) (t01 / det);
+        output[2] = (float) (t02 / det);
+        output[3] = (float) (t10 / det);
+        output[4] = (float) (t11 / det);
+        output[5] = (float) (t12 / det);
+        output[6] = (float) (t20 / det);
+        output[7] = (float) (t21 / det);
+        output[8] = (float) (t22 / det);
+        return true;
+    }
+
+    /**
+     * Scale each element in a matrix by the given scaling factor.
+     *
+     * @param factor factor to scale by.
+     * @param matrix the float array containing a 3x3 matrix to scale.
+     */
+    private static void scale(float factor, /*inout*/float[] matrix) {
+        for (int i = 0; i < 9; i++) {
+            matrix[i] *= factor;
+        }
+    }
+
+    /**
+     * Clamp a value to a given range.
+     *
+     * @param low lower bound to clamp to.
+     * @param high higher bound to clamp to.
+     * @param value the value to clamp.
+     * @return the clamped value.
+     */
+    private static double clamp(double low, double high, double value) {
+        return Math.max(low, Math.min(high, value));
+    }
+
+    /**
+     * Return the max float in the array.
+     *
+     * @param array array of floats to search.
+     * @return max float in the array.
+     */
+    private static float max(float[] array) {
+        float val = array[0];
+        for (float f : array) {
+            val = (f > val) ? f : val;
+        }
+        return val;
+    }
+
+    /**
+     * Normalize ColorMatrix to eliminate headroom for input space scaled to [0, 1] using
+     * the D50 whitepoint.  This maps the D50 whitepoint into the colorspace used by the
+     * ColorMatrix, then uses the resulting whitepoint to renormalize the ColorMatrix so
+     * that the channel values in the resulting whitepoint for this operation are clamped
+     * to the range [0, 1].
+     *
+     * @param colorMatrix a 3x3 matrix containing a DNG ColorMatrix to be normalized.
+     */
+    private static void normalizeCM(/*inout*/float[] colorMatrix) {
+        float[] tmp = new float[3];
+        map(colorMatrix, D50_XYZ, /*out*/tmp);
+        float maxVal = max(tmp);
+        if (maxVal > 0) {
+            scale(1.0f / maxVal, colorMatrix);
+        }
+    }
+
+    /**
+     * Normalize ForwardMatrix to ensure that sensor whitepoint [1, 1, 1] maps to D50 in CIE XYZ
+     * colorspace.
+     *
+     * @param forwardMatrix a 3x3 matrix containing a DNG ForwardTransform to be normalized.
+     */
+    private static void normalizeFM(/*inout*/float[] forwardMatrix) {
+        float[] tmp = new float[] {1, 1, 1};
+        float[] xyz = new float[3];
+        map(forwardMatrix, tmp, /*out*/xyz);
+
+        float[] intermediate = new float[9];
+        float[] m = new float[] {1.0f / xyz[0], 0, 0, 0, 1.0f / xyz[1], 0, 0, 0, 1.0f / xyz[2]};
+
+        multiply(m, forwardMatrix, /*out*/ intermediate);
+        float[] m2 = new float[] {D50_XYZ[0], 0, 0, 0, D50_XYZ[1], 0, 0, 0, D50_XYZ[2]};
+        multiply(m2, intermediate, /*out*/forwardMatrix);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
new file mode 100644
index 0000000..c8b353e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../common.rs"
+
+// This file includes a conversion kernel for RGGB, GRBG, GBRG, and BGGR Bayer patterns.
+// Applying this script also will apply black-level subtraction, rescaling, clipping, tonemapping,
+// and color space transforms along with the Bayer demosaic.  See RawConverter.java
+// for more information.
+
+// Input globals
+
+rs_allocation inputRawBuffer; // RAW16 buffer of dimensions (raw image stride) * (raw image height)
+rs_allocation gainMap; // Gainmap to apply to linearized raw sensor data.
+uint cfaPattern; // The Color Filter Arrangement pattern used
+uint gainMapWidth;  // The width of the gain map
+uint gainMapHeight;  // The height of the gain map
+bool hasGainMap; // Does gainmap exist?
+rs_matrix3x3 sensorToIntermediate; // Color transform from sensor to a wide-gamut colorspace
+rs_matrix3x3 intermediateToSRGB; // Color transform from wide-gamut colorspace to sRGB
+ushort4 blackLevelPattern; // Blacklevel to subtract for each channel, given in CFA order
+int whiteLevel;  // Whitelevel of sensor
+uint offsetX; // X offset into inputRawBuffer
+uint offsetY; // Y offset into inputRawBuffer
+uint rawWidth; // Width of raw buffer
+uint rawHeight; // Height of raw buffer
+float3 neutralPoint; // The camera neutral
+float4 toneMapCoeffs; // Coefficients for a polynomial tonemapping curve
+
+// Interpolate gain map to find per-channel gains at a given pixel
+static float4 getGain(uint x, uint y) {
+    float interpX = (((float) x) / rawWidth) * gainMapWidth;
+    float interpY = (((float) y) / rawHeight) * gainMapHeight;
+    uint gX = (uint) interpX;
+    uint gY = (uint) interpY;
+    uint gXNext = (gX + 1 < gainMapWidth) ? gX + 1 : gX;
+    uint gYNext = (gY + 1 < gainMapHeight) ? gY + 1 : gY;
+
+    float4 tl = *((float4 *) rsGetElementAt(gainMap, gX, gY));
+    float4 tr = *((float4 *) rsGetElementAt(gainMap, gXNext, gY));
+    float4 bl = *((float4 *) rsGetElementAt(gainMap, gX, gYNext));
+    float4 br = *((float4 *) rsGetElementAt(gainMap, gXNext, gYNext));
+
+    float fracX = interpX - (float) gX;
+    float fracY = interpY - (float) gY;
+    float invFracX = 1.f - fracX;
+    float invFracY = 1.f - fracY;
+
+    return tl * invFracX * invFracY + tr * fracX * invFracY +
+            bl * invFracX * fracY + br * fracX * fracY;
+}
+
+// Apply gamma correction using sRGB gamma curve
+static float gammaEncode(float x) {
+    return (x <= 0.0031308f) ? x * 12.92f : 1.055f * pow(x, 0.4166667f) - 0.055f;
+}
+
+// Apply gamma correction to each color channel in RGB pixel
+static float3 gammaCorrectPixel(float3 rgb) {
+    float3 ret;
+    ret.x = gammaEncode(rgb.x);
+    ret.y = gammaEncode(rgb.y);
+    ret.z = gammaEncode(rgb.z);
+    return ret;
+}
+
+// Apply polynomial tonemapping curve to each color channel in RGB pixel.
+// This attempts to apply tonemapping without changing the hue of each pixel,
+// i.e.:
+//
+// For some RGB values:
+// M = max(R, G, B)
+// m = min(R, G, B)
+// m' = mid(R, G, B)
+// chroma = M - m
+// H = m' - m / chroma
+//
+// The relationship H=H' should be preserved, where H and H' are calculated from
+// the RGB and RGB' value at this pixel before and after this tonemapping
+// operation has been applied, respectively.
+static float3 tonemap(float3 rgb) {
+    float3 sorted = clamp(rgb, 0.f, 1.f);
+    float tmp;
+    int permutation = 0;
+
+    // Sort the RGB channels by value
+    if (sorted.z < sorted.y) {
+        tmp = sorted.z;
+        sorted.z = sorted.y;
+        sorted.y = tmp;
+        permutation |= 1;
+    }
+    if (sorted.y < sorted.x) {
+        tmp = sorted.y;
+        sorted.y = sorted.x;
+        sorted.x = tmp;
+        permutation |= 2;
+    }
+    if (sorted.z < sorted.y) {
+        tmp = sorted.z;
+        sorted.z = sorted.y;
+        sorted.y = tmp;
+        permutation |= 4;
+    }
+
+    float2 minmax;
+    minmax.x = sorted.x;
+    minmax.y = sorted.z;
+
+    // Apply tonemapping curve to min, max RGB channel values
+    minmax = native_powr(minmax, 3.f) * toneMapCoeffs.x +
+            native_powr(minmax, 2.f) * toneMapCoeffs.y +
+            minmax * toneMapCoeffs.z + toneMapCoeffs.w;
+
+    // Rescale middle value
+    float newMid;
+    if (sorted.z == sorted.x) {
+        newMid = minmax.y;
+    } else {
+        newMid = minmax.x + ((minmax.y - minmax.x) * (sorted.y - sorted.x) /
+                (sorted.z - sorted.x));
+    }
+
+    float3 finalRGB;
+    switch (permutation) {
+        case 0: // b >= g >= r
+            finalRGB.x = minmax.x;
+            finalRGB.y = newMid;
+            finalRGB.z = minmax.y;
+            break;
+        case 1: // g >= b >= r
+            finalRGB.x = minmax.x;
+            finalRGB.z = newMid;
+            finalRGB.y = minmax.y;
+            break;
+        case 2: // b >= r >= g
+            finalRGB.y = minmax.x;
+            finalRGB.x = newMid;
+            finalRGB.z = minmax.y;
+            break;
+        case 3: // g >= r >= b
+            finalRGB.z = minmax.x;
+            finalRGB.x = newMid;
+            finalRGB.y = minmax.y;
+            break;
+        case 6: // r >= b >= g
+            finalRGB.y = minmax.x;
+            finalRGB.z = newMid;
+            finalRGB.x = minmax.y;
+            break;
+        case 7: // r >= g >= b
+            finalRGB.z = minmax.x;
+            finalRGB.y = newMid;
+            finalRGB.x = minmax.y;
+            break;
+        case 4: // impossible
+        case 5: // impossible
+        default:
+            LOGD("raw_converter.rs: Logic error in tonemap.", 0);
+            break;
+    }
+    return clamp(finalRGB, 0.f, 1.f);
+}
+
+// Apply a colorspace transform to the intermediate colorspace, apply
+// a tonemapping curve, apply a colorspace transform to a final colorspace,
+// and apply a gamma correction curve.
+static float3 applyColorspace(float3 pRGB) {
+    pRGB.x = clamp(pRGB.x, 0.f, neutralPoint.x);
+    pRGB.y = clamp(pRGB.y, 0.f, neutralPoint.y);
+    pRGB.z = clamp(pRGB.z, 0.f, neutralPoint.z);
+
+    float3 intermediate = rsMatrixMultiply(&sensorToIntermediate, pRGB);
+    intermediate = tonemap(intermediate);
+    return gammaCorrectPixel(clamp(rsMatrixMultiply(&intermediateToSRGB, intermediate), 0.f, 1.f));
+}
+
+// Load a 3x3 patch of pixels into the output.
+static void load3x3(uint x, uint y, rs_allocation buf, /*out*/float* outputArray) {
+    outputArray[0] = *((ushort *) rsGetElementAt(buf, x - 1, y - 1));
+    outputArray[1] = *((ushort *) rsGetElementAt(buf, x, y - 1));
+    outputArray[2] = *((ushort *) rsGetElementAt(buf, x + 1, y - 1));
+    outputArray[3] = *((ushort *) rsGetElementAt(buf, x - 1, y));
+    outputArray[4] = *((ushort *) rsGetElementAt(buf, x, y));
+    outputArray[5] = *((ushort *) rsGetElementAt(buf, x + 1, y));
+    outputArray[6] = *((ushort *) rsGetElementAt(buf, x - 1, y + 1));
+    outputArray[7] = *((ushort *) rsGetElementAt(buf, x, y + 1));
+    outputArray[8] = *((ushort *) rsGetElementAt(buf, x + 1, y + 1));
+}
+
+// Blacklevel subtract, and normalize each pixel in the outputArray, and apply the
+// gain map.
+static void linearizeAndGainmap(uint x, uint y, ushort4 blackLevel, int whiteLevel,
+        uint cfa, /*inout*/float* outputArray) {
+    uint kk = 0;
+    for (uint j = y - 1; j <= y + 1; j++) {
+        for (uint i = x - 1; i <= x + 1; i++) {
+            uint index = (i & 1) | ((j & 1) << 1);  // bits [0,1] are blacklevel offset
+            index |= (cfa << 2);  // bits [2,3] are cfa
+            float bl = 0.f;
+            float g = 1.f;
+            float4 gains = 1.f;
+            if (hasGainMap) {
+                gains = getGain(i, j);
+            }
+            switch (index) {
+                // RGGB
+                case 0:
+                    bl = blackLevel.x;
+                    g = gains.x;
+                    break;
+                case 1:
+                    bl = blackLevel.y;
+                    g = gains.y;
+                    break;
+                case 2:
+                    bl = blackLevel.z;
+                    g = gains.z;
+                    break;
+                case 3:
+                    bl = blackLevel.w;
+                    g = gains.w;
+                    break;
+                // GRBG
+                case 4:
+                    bl = blackLevel.x;
+                    g = gains.y;
+                    break;
+                case 5:
+                    bl = blackLevel.y;
+                    g = gains.x;
+                    break;
+                case 6:
+                    bl = blackLevel.z;
+                    g = gains.w;
+                    break;
+                case 7:
+                    bl = blackLevel.w;
+                    g = gains.z;
+                    break;
+                // GBRG
+                case 8:
+                    bl = blackLevel.x;
+                    g = gains.y;
+                    break;
+                case 9:
+                    bl = blackLevel.y;
+                    g = gains.w;
+                    break;
+                case 10:
+                    bl = blackLevel.z;
+                    g = gains.x;
+                    break;
+                case 11:
+                    bl = blackLevel.w;
+                    g = gains.z;
+                    break;
+                // BGGR
+                case 12:
+                    bl = blackLevel.x;
+                    g = gains.w;
+                    break;
+                case 13:
+                    bl = blackLevel.y;
+                    g = gains.y;
+                    break;
+                case 14:
+                    bl = blackLevel.z;
+                    g = gains.z;
+                    break;
+                case 15:
+                    bl = blackLevel.w;
+                    g = gains.x;
+                    break;
+            }
+            outputArray[kk] = clamp(g * (outputArray[kk] - bl) / (whiteLevel - bl), 0.f, 1.f);
+            kk++;
+        }
+    }
+}
+
+// Apply bilinear-interpolation to demosaic
+static float3 demosaic(uint x, uint y, uint cfa, float* inputArray) {
+    uint index = (x & 1) | ((y & 1) << 1);
+    index |= (cfa << 2);
+    float3 pRGB;
+    switch (index) {
+        case 0:
+        case 5:
+        case 10:
+        case 15:  // Red centered
+                  // B G B
+                  // G R G
+                  // B G B
+            pRGB.x = inputArray[4];
+            pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+            pRGB.z = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+            break;
+        case 1:
+        case 4:
+        case 11:
+        case 14: // Green centered w/ horizontally adjacent Red
+                 // G B G
+                 // R G R
+                 // G B G
+            pRGB.x = (inputArray[3] + inputArray[5]) / 2;
+            pRGB.y = inputArray[4];
+            pRGB.z = (inputArray[1] + inputArray[7]) / 2;
+            break;
+        case 2:
+        case 7:
+        case 8:
+        case 13: // Green centered w/ horizontally adjacent Blue
+                 // G R G
+                 // B G B
+                 // G R G
+            pRGB.x = (inputArray[1] + inputArray[7]) / 2;
+            pRGB.y = inputArray[4];
+            pRGB.z = (inputArray[3] + inputArray[5]) / 2;
+            break;
+        case 3:
+        case 6:
+        case 9:
+        case 12: // Blue centered
+                 // R G R
+                 // G B G
+                 // R G R
+            pRGB.x = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+            pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+            pRGB.z = inputArray[4];
+            break;
+    }
+
+    return pRGB;
+}
+
+// Full RAW->ARGB bitmap conversion kernel
+uchar4 RS_KERNEL convert_RAW_To_ARGB(uint x, uint y) {
+    float3 pRGB;
+    uint xP = x + offsetX;
+    uint yP = y + offsetY;
+    if (xP == 0) xP = 1;
+    if (yP == 0) yP = 1;
+    if (xP == rawWidth - 1) xP = rawWidth - 2;
+    if (yP == rawHeight - 1) yP = rawHeight  - 2;
+
+    float patch[9];
+    // TODO: Once ScriptGroup and RS kernels have been updated to allow for iteration over 3x3 pixel
+    // patches, this can be optimized to avoid re-applying the pre-demosaic steps for each pixel,
+    // potentially achieving a 9x speedup here.
+    load3x3(xP, yP, inputRawBuffer, /*out*/ patch);
+    linearizeAndGainmap(xP, yP, blackLevelPattern, whiteLevel, cfaPattern, /*inout*/patch);
+    pRGB = demosaic(xP, yP, cfaPattern, patch);
+
+    return rsPackColorTo8888(applyColorspace(pRGB));
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
index e2b7561..7640cd7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
@@ -288,10 +288,9 @@
             TestSensorOperation operation,
             boolean flushExpected) throws Throwable {
         operation.addDefaultVerifications();
-        operation.setLogEvents(true);
 
         try {
-            operation.execute();
+            operation.execute(getCurrentTestNode());
         } finally {
             SensorStats stats = operation.getStats();
             stats.log(TAG);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index 1f070c4..8c3fb7a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
 import android.hardware.cts.helpers.sensoroperations.RepeatingSensorOperation;
@@ -96,7 +95,7 @@
             batchingOperation.addVerification(new EventOrderingVerification());
             operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
         }
-        operation.execute();
+        operation.execute(getCurrentTestNode());
         operation.getStats().log(TAG);
     }
 
@@ -152,7 +151,7 @@
             }
         }
 
-        operation.execute();
+        operation.execute(getCurrentTestNode());
         operation.getStats().log(TAG);
     }
 
@@ -242,11 +241,11 @@
 
         ParallelSensorOperation operation = new ParallelSensorOperation();
         operation.add(tester, testee);
-        operation.execute();
+        operation.execute(getCurrentTestNode());
         operation.getStats().log(TAG);
 
         testee = testee.clone();
-        testee.execute();
+        testee.execute(getCurrentTestNode());
         testee.getStats().log(TAG);
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 87c74ee..d606b70 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -47,6 +47,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class SensorTest extends SensorTestCase {
@@ -329,10 +330,10 @@
         Handler handler = new Handler(handlerThread.getLooper());
         TestSensorEventListener listener = new TestSensorEventListener(environment, handler);
 
-        sensorManager.registerListener(listener);
-        listener.waitForEvents(1);
-        sensorManager.requestFlush();
-        listener.waitForFlushComplete();
+        CountDownLatch eventLatch = sensorManager.registerListener(listener, 1);
+        listener.waitForEvents(eventLatch, 1);
+        CountDownLatch flushLatch = sensorManager.requestFlush();
+        listener.waitForFlushComplete(flushLatch);
         listener.assertEventsReceivedInHandler();
     }
 
@@ -341,30 +342,34 @@
     public void testBatchAndFlushWithMutipleSensors() throws Exception {
         final int maxSensors = 3;
         final int maxReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(10);
-        int sensorsCount = mSensorList.size();
-        int numSensors = sensorsCount < maxSensors ? sensorsCount : maxSensors;
+        List<Sensor> sensorsToTest = new ArrayList<Sensor>();
+        for (Sensor sensor : mSensorList) {
+            if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+                sensorsToTest.add(sensor);
+                if (sensorsToTest.size()  == maxSensors) break;
+            }
+        }
+        final int numSensorsToTest = sensorsToTest.size();
+        if (numSensorsToTest == 0) {
+            return;
+        }
 
         StringBuilder builder = new StringBuilder();
         ParallelSensorOperation parallelSensorOperation = new ParallelSensorOperation();
-        for (int i = 0; i < sensorsCount && numSensors > 0; ++i) {
-            Sensor sensor = mSensorList.get(i);
-            // skip all non-continuous sensors
-            if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
-                TestSensorEnvironment environment = new TestSensorEnvironment(
-                        getContext(),
-                        sensor,
-                        shouldEmulateSensorUnderLoad(),
-                        SensorManager.SENSOR_DELAY_FASTEST,
-                        maxReportLatencyUs);
-                FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
-                parallelSensorOperation.add(new TestSensorOperation(environment, executor));
-                --numSensors;
-                builder.append(sensor.getName()).append(", ");
-            }
+        for (Sensor sensor : sensorsToTest) {
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    getContext(),
+                    sensor,
+                    shouldEmulateSensorUnderLoad(),
+                    SensorManager.SENSOR_DELAY_FASTEST,
+                    maxReportLatencyUs);
+            FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+            parallelSensorOperation.add(new TestSensorOperation(environment, executor));
+            builder.append(sensor.getName()).append(", ");
         }
 
         Log.i(TAG, "Testing batch/flush for sensors: " + builder);
-        parallelSensorOperation.execute();
+        parallelSensorOperation.execute(getCurrentTestNode());
     }
 
     private void assertSensorValues(Sensor sensor) {
@@ -436,7 +441,7 @@
                     EventTimestampSynchronizationVerification.getDefault(environment));
 
             Log.i(TAG, "Running timestamp test on: " + sensor.getName());
-            operation.execute();
+            operation.execute(getCurrentTestNode());
         } catch (InterruptedException e) {
             // propagate so the test can stop
             throw e;
@@ -471,7 +476,7 @@
             TestSensorOperation operation = new TestSensorOperation(environment, executor, handler);
 
             Log.i(TAG, "Running flush test on: " + sensor.getName());
-            operation.execute();
+            operation.execute(getCurrentTestNode());
         } catch (InterruptedException e) {
             // propagate so the test can stop
             throw e;
@@ -523,12 +528,12 @@
                 throws InterruptedException {
             int sensorReportingMode = mEnvironment.getSensor().getReportingMode();
             try {
-                sensorManager.registerListener(listener);
+                CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount);
                 if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
-                    listener.waitForEvents(mEventCount);
+                    listener.waitForEvents(eventLatch, mEventCount);
                 }
-                sensorManager.requestFlush();
-                listener.waitForFlushComplete();
+                CountDownLatch flushLatch = sensorManager.requestFlush();
+                listener.waitForFlushComplete(flushLatch);
             } finally {
                 sensorManager.unregisterListener();
             }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
index 8dba5d6..42b8d33 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -16,16 +16,10 @@
 
 package android.hardware.cts;
 
-import com.android.cts.util.ReportLog;
-import com.android.cts.util.ResultType;
-import com.android.cts.util.ResultUnit;
-
-import android.app.Instrumentation;
-import android.cts.util.DeviceReportLog;
 import android.hardware.Sensor;
-import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
 import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.hardware.cts.helpers.sensoroperations.SensorOperation;
 import android.test.AndroidTestCase;
 import android.util.Log;
@@ -48,6 +42,11 @@
      */
     private volatile boolean mEmulateSensorUnderLoad = true;
 
+    /**
+     * By default the test class is the root of the test hierarchy.
+     */
+    private volatile ISensorTestNode mCurrentTestNode = new TestClassNode(getClass());
+
     protected SensorTestCase() {}
 
     @Override
@@ -68,33 +67,24 @@
         return mEmulateSensorUnderLoad;
     }
 
-    /**
-     * Utility method to log selected stats to a {@link ReportLog} object.  The stats must be
-     * a number or an array of numbers.
-     */
-    public static void logSelectedStatsToReportLog(Instrumentation instrumentation, int depth,
-            String[] keys, SensorStats stats) {
-        DeviceReportLog reportLog = new DeviceReportLog(depth);
+    public void setCurrentTestNode(ISensorTestNode value) {
+        mCurrentTestNode = value;
+    }
 
-        for (String key : keys) {
-            Object value = stats.getValue(key);
-            if (value instanceof Integer) {
-                reportLog.printValue(key, (Integer) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof Double) {
-                reportLog.printValue(key, (Double) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof Float) {
-                reportLog.printValue(key, (Float) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof double[]) {
-                reportLog.printArray(key, (double[]) value, ResultType.NEUTRAL, ResultUnit.NONE);
-            } else if (value instanceof float[]) {
-                float[] tmpFloat = (float[]) value;
-                double[] tmpDouble = new double[tmpFloat.length];
-                for (int i = 0; i < tmpDouble.length; i++) tmpDouble[i] = tmpFloat[i];
-                reportLog.printArray(key, tmpDouble, ResultType.NEUTRAL, ResultUnit.NONE);
-            }
+    protected ISensorTestNode getCurrentTestNode() {
+        return mCurrentTestNode;
+    }
+
+    private class TestClassNode implements ISensorTestNode {
+        private final Class<?> mTestClass;
+
+        public TestClassNode(Class<?> testClass) {
+            mTestClass = testClass;
         }
 
-        reportLog.printSummary("summary", 0, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.deliverReportToHost(instrumentation);
+        @Override
+        public String getName() {
+            return mTestClass.getSimpleName();
+        }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index 23feef8..42cbdfb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -544,9 +544,9 @@
         TestSensorOperation op =
                 TestSensorOperation.createOperation(environment, 5, TimeUnit.SECONDS);
         op.addDefaultVerifications();
-        op.setLogEvents(true);
+
         try {
-            op.execute();
+            op.execute(getCurrentTestNode());
         } finally {
             SensorStats stats = op.getStats();
             stats.log(TAG);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
index e8500f1..86b2436 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
@@ -21,6 +21,8 @@
 import android.hardware.SensorEventListener2;
 import android.os.SystemClock;
 
+import java.util.Arrays;
+
 /**
  * Class for holding information about individual {@link SensorEvent}s.
  */
@@ -75,4 +77,14 @@
         this.accuracy = accuracy;
         this.values = values;
     }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "Timestamp=%sns, ReceivedTimestamp=%sns, Accuracy=%s, Values=%s",
+                this.timestamp,
+                this.receivedTimestamp,
+                this.accuracy,
+                Arrays.toString(this.values));
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 6c313d2..7b1a499 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -25,7 +25,12 @@
 import android.os.Looper;
 import android.os.SystemClock;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -40,6 +45,7 @@
  */
 public class TestSensorEventListener implements SensorEventListener2 {
     public static final String LOG_TAG = "TestSensorEventListener";
+
     private static final long EVENT_TIMEOUT_US = TimeUnit.SECONDS.toMicros(5);
     private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
 
@@ -84,7 +90,6 @@
         synchronized (mCollectedEvents) {
             mCollectedEvents.add(new TestSensorEvent(event, timestampNs));
         }
-
         synchronized (mEventLatches) {
             for (CountDownLatch latch : mEventLatches) {
                 latch.countDown();
@@ -101,6 +106,31 @@
     }
 
     /**
+     * @param eventCount
+     * @return A CountDownLatch initialzed with eventCount and decremented as sensor events arrive
+     * for this listerner.
+     */
+    public CountDownLatch getLatchForSensorEvents(int eventCount) {
+        CountDownLatch latch = new CountDownLatch(eventCount);
+        synchronized (mEventLatches) {
+            mEventLatches.add(latch);
+        }
+        return latch;
+    }
+
+    /**
+     * @return A CountDownLatch initialzed with 1 and decremented as a flush complete arrives
+     * for this listerner.
+     */
+    public CountDownLatch getLatchForFlushCompleteEvent() {
+        CountDownLatch latch = new CountDownLatch(1);
+        synchronized (mFlushLatches) {
+            mFlushLatches.add(latch);
+        }
+        return latch;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -138,18 +168,47 @@
         }
     }
 
+
+    /**
+     * Utility method to log the collected events to a file.
+     * It will overwrite the file if it already exists, the file is created in a relative directory
+     * named 'events' under the sensor test directory (part of external storage).
+     */
+    public void logCollectedEventsToFile(String fileName) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Sensor='").append(mEnvironment.getSensor()).append("', ");
+        builder.append("SamplingRateOverloaded=")
+                .append(mEnvironment.isSensorSamplingRateOverloaded()).append(", ");
+        builder.append("RequestedSamplingPeriod=")
+                .append(mEnvironment.getRequestedSamplingPeriodUs()).append("us, ");
+        builder.append("MaxReportLatency=")
+                .append(mEnvironment.getMaxReportLatencyUs()).append("us");
+
+        synchronized (mCollectedEvents) {
+            for (TestSensorEvent event : mCollectedEvents) {
+                builder.append("\n");
+                builder.append("Timestamp=").append(event.timestamp).append("ns, ");
+                builder.append("ReceivedTimestamp=").append(event.receivedTimestamp).append("ns, ");
+                builder.append("Accuracy=").append(event.accuracy).append(", ");
+                builder.append("Values=").append(Arrays.toString(event.values));
+            }
+        }
+
+        File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory("events/");
+        File logFile = new File(eventsDirectory, fileName);
+        FileWriter fileWriter = new FileWriter(logFile, false /* append */);
+        try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
+            writer.write(builder.toString());
+        }
+    }
+
     /**
      * Wait for {@link #onFlushCompleted(Sensor)} to be called.
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForFlushComplete() throws InterruptedException {
+    public void waitForFlushComplete(CountDownLatch latch) throws InterruptedException {
         clearEvents();
-        CountDownLatch latch = new CountDownLatch(1);
-        synchronized (mFlushLatches) {
-            mFlushLatches.add(latch);
-        }
-
         try {
             String message = SensorCtsHelper.formatAssertionMessage(
                     "WaitForFlush",
@@ -169,13 +228,8 @@
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForEvents(int eventCount) throws InterruptedException {
+    public void waitForEvents(CountDownLatch latch, int eventCount) throws InterruptedException {
         clearEvents();
-        CountDownLatch eventLatch = new CountDownLatch(eventCount);
-        synchronized (mEventLatches) {
-            mEventLatches.add(eventLatch);
-        }
-
         try {
             long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
             // timeout is 2 * event count * expected period + batch timeout + default wait
@@ -185,20 +239,20 @@
             long timeoutUs = (2 * eventCount * samplingPeriodUs)
                     + mEnvironment.getMaxReportLatencyUs()
                     + EVENT_TIMEOUT_US;
-            boolean success = eventLatch.await(timeoutUs, TimeUnit.MICROSECONDS);
+            boolean success = latch.await(timeoutUs, TimeUnit.MICROSECONDS);
             if (!success) {
                 String message = SensorCtsHelper.formatAssertionMessage(
                         "WaitForEvents",
                         mEnvironment,
                         "requested=%d, received=%d, timeout=%dus",
                         eventCount,
-                        eventCount - eventLatch.getCount(),
+                        eventCount - latch.getCount(),
                         timeoutUs);
                 Assert.fail(message);
             }
         } finally {
             synchronized (mEventLatches) {
-                mEventLatches.remove(eventLatch);
+                mEventLatches.remove(latch);
             }
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
index fdd851e..2468bd1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -23,6 +23,8 @@
 import android.hardware.SensorManager;
 import android.util.Log;
 
+import java.util.concurrent.CountDownLatch;
+
 /**
  * A test class that performs the actions of {@link SensorManager} on a single sensor.
  * This class allows for a single sensor to be registered and unregistered as well as performing
@@ -85,6 +87,33 @@
     }
 
     /**
+     * Register the listener. This method will perform a no-op if the sensor is already registered.
+     *
+     * @return A CountDownLatch initialized with eventCount which is used to wait for sensor
+     * events.
+     * @throws AssertionError if there was an error registering the listener with the
+     * {@link SensorManager}
+     */
+    public CountDownLatch registerListener(TestSensorEventListener listener, int eventCount) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return null;
+        }
+
+        CountDownLatch latch = listener.getLatchForSensorEvents(eventCount);
+        mTestSensorEventListener = listener;
+        String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
+        boolean result = mSensorManager.registerListener(
+                mTestSensorEventListener,
+                mEnvironment.getSensor(),
+                mEnvironment.getRequestedSamplingPeriodUs(),
+                mEnvironment.getMaxReportLatencyUs(),
+                mTestSensorEventListener.getHandler());
+        Assert.assertTrue(message, result);
+        return latch;
+    }
+
+    /**
      * Unregister the listener. This method will perform a no-op if the sensor is not registered.
      */
     public void unregisterListener() {
@@ -101,15 +130,18 @@
      * Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
      * the sensor is not registered.
      *
+     * @return A CountDownLatch which can be used to wait for a flush complete event.
      * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} fails.
      */
-    public void requestFlush() {
+    public CountDownLatch requestFlush() {
         if (mTestSensorEventListener == null) {
             Log.w(LOG_TAG, "No listener registered, returning.");
-            return;
+            return null;
         }
+        CountDownLatch latch = mTestSensorEventListener.getLatchForFlushCompleteEvent();
         Assert.assertTrue(
                 SensorCtsHelper.formatAssertionMessage("Flush", mEnvironment),
                 mSensorManager.flush(mTestSensorEventListener));
+        return latch;
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java b/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java
new file mode 100644
index 0000000..d34c0af
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.reporting;
+
+import android.hardware.cts.helpers.SensorTestPlatformException;
+
+/**
+ * Interface that represents a node in a hierarchy built by the sensor test platform.
+ */
+// TODO: this is an intermediate state to introduce a full-blown centralized recorder data produced
+//       by sensor tests
+public interface ISensorTestNode {
+
+    /**
+     * Provides a name (tag) that can be used to identify the current node.
+     */
+    String getName() throws SensorTestPlatformException;
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
index 8848337..436a7cf 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -22,7 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 
@@ -76,7 +76,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
         // Start alarm
         IntentFilter intentFilter = new IntentFilter(ACTION);
         BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -96,7 +96,7 @@
 
         // Execute operation
         try {
-            mOperation.execute();
+            mOperation.execute(asTestNode(parent));
         } finally {
             releaseWakeLock();
         }
@@ -106,14 +106,6 @@
      * {@inheritDoc}
      */
     @Override
-    public SensorStats getStats() {
-        return mOperation.getStats();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public AlarmOperation clone() {
         return new AlarmOperation(mOperation, mContext, mSleepDuration, mTimeUnit);
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
index 3ee08f6..8c52222 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -17,7 +17,7 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import java.util.concurrent.TimeUnit;
 
@@ -38,9 +38,7 @@
      * @param timeUnit the unit of the delay
      */
     public DelaySensorOperation(SensorOperation operation, long delay, TimeUnit timeUnit) {
-        if (operation == null || timeUnit == null) {
-            throw new IllegalArgumentException("Arguments cannot be null");
-        }
+        super(operation.getStats());
         mOperation = operation;
         mDelay = delay;
         mTimeUnit = timeUnit;
@@ -50,17 +48,9 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
         SensorCtsHelper.sleep(mDelay, mTimeUnit);
-        mOperation.execute();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public SensorStats getStats() {
-        return mOperation.getStats();
+        mOperation.execute(asTestNode(parent));
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
index 8cfd351..238956b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -16,9 +16,10 @@
 
 package android.hardware.cts.helpers.sensoroperations;
 
-import junit.framework.Assert;
-
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+
+import junit.framework.Assert;
 
 import java.util.concurrent.TimeUnit;
 
@@ -56,7 +57,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() {
+    public void execute(ISensorTestNode parent) {
         long delayNs = TimeUnit.NANOSECONDS.convert(mDelay, mTimeUnit);
         try {
             Thread.sleep(delayNs / NANOS_PER_MILLI, (int) (delayNs % NANOS_PER_MILLI));
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
index e55c6cb..ed70b70 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -19,6 +19,7 @@
 import junit.framework.Assert;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.os.SystemClock;
 
 import java.util.ArrayList;
@@ -80,7 +81,7 @@
      * operations, the first exception will be thrown once all operations are completed.
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(final ISensorTestNode parent) throws InterruptedException {
         int operationsCount = mOperations.size();
         ThreadPoolExecutor executor = new ThreadPoolExecutor(
                 operationsCount,
@@ -91,12 +92,13 @@
         executor.allowCoreThreadTimeOut(true);
         executor.prestartAllCoreThreads();
 
+        final ISensorTestNode currentNode = asTestNode(parent);
         ArrayList<Future<SensorOperation>> futures = new ArrayList<>();
         for (final SensorOperation operation : mOperations) {
             Future<SensorOperation> future = executor.submit(new Callable<SensorOperation>() {
                 @Override
                 public SensorOperation call() throws Exception {
-                    operation.execute();
+                    operation.execute(currentNode);
                     return operation;
                 }
             });
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
index 2e3af36..5b333b8 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -17,6 +17,8 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 /**
  * A {@link SensorOperation} that executes a single {@link SensorOperation} a given number of
@@ -40,7 +42,6 @@
         }
         mOperation = operation;
         mIterations = iterations;
-
     }
 
     /**
@@ -48,11 +49,12 @@
      * one iterations, it is thrown and all subsequent iterations will not run.
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
+        ISensorTestNode currentNode = asTestNode(parent);
         for(int i = 0; i < mIterations; ++i) {
             SensorOperation operation = mOperation.clone();
             try {
-                operation.execute();
+                operation.execute(new TestNode(currentNode, i));
             } catch (AssertionError e) {
                 String msg = String.format("Iteration %d failed: \"%s\"", i, e.getMessage());
                 getStats().addValue(SensorStats.ERROR, msg);
@@ -70,4 +72,19 @@
     public RepeatingSensorOperation clone() {
         return new RepeatingSensorOperation(mOperation.clone(), mIterations);
     }
+
+    private class TestNode implements ISensorTestNode {
+        private final ISensorTestNode mTestNode;
+        private final int mIteration;
+
+        public TestNode(ISensorTestNode parent, int iteration) {
+            mTestNode = asTestNode(parent);
+            mIteration = iteration;
+        }
+
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return String.format("%s-iteration%d", mTestNode.getName(), mIteration);
+        }
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
index ea16716..66604d3 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
@@ -17,6 +17,8 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 /**
  * Base class used by all sensor operations. This allows for complex operations such as chaining
@@ -24,11 +26,12 @@
  * <p>
  * Certain restrictions exist for {@link SensorOperation}s:
  * <p><ul>
- * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
- * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
- * synchronization for operations.</li>
- * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
- * the returned value is undefined.</li>
+ * <li>{@link #execute(ISensorTestNode)} should only be called once and behavior is undefined for
+ * subsequent calls.
+ * Once {@link #execute(ISensorTestNode)} is called, the class should not be modified. Generally,
+ * there is no synchronization for operations.</li>
+ * <li>{@link #getStats()} should only be called after {@link #execute(ISensorTestNode)}. If it
+ * is called before, the returned value is undefined.</li>
  * <li>{@link #clone()} may be called any time and should return an operation with the same
  * parameters as the original.</li>
  * </ul>
@@ -59,7 +62,7 @@
      * - cleaning up on {@link InterruptedException}
      * - propagating the exception down the stack
      */
-    public abstract void execute() throws InterruptedException;
+    public abstract void execute(ISensorTestNode parent) throws InterruptedException;
 
     /**
      * @return The cloned {@link SensorOperation}.
@@ -85,4 +88,23 @@
     protected void addSensorStats(String key, int index, SensorStats stats) {
         addSensorStats(String.format("%s_%03d", key, index), stats);
     }
+
+    protected ISensorTestNode asTestNode(ISensorTestNode parent) {
+        return new SensorTestNode(parent, this);
+    }
+
+    private class SensorTestNode implements ISensorTestNode {
+        private final ISensorTestNode mParent;
+        private final SensorOperation mOperation;
+
+        public SensorTestNode(ISensorTestNode parent, SensorOperation operation) {
+            mParent = parent;
+            mOperation = operation;
+        }
+
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return mParent.getName() + "-" + mOperation.getClass().getSimpleName();
+        }
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
index 033f3c5..30da9a0 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -19,6 +19,8 @@
 import junit.framework.TestCase;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -31,6 +33,13 @@
 public class SensorOperationTest extends TestCase {
     private static final long TEST_DURATION_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(5);
 
+    private final ISensorTestNode mTestNode = new ISensorTestNode() {
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return "SensorOperationUnitTest";
+        }
+    };
+
     /**
      * Test that the {@link FakeSensorOperation} functions correctly. Other tests in this class
      * rely on this operation.
@@ -42,14 +51,14 @@
 
         assertFalse(op.getStats().flatten().containsKey("executed"));
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long duration = System.currentTimeMillis() - start;
         assertTrue(Math.abs(opDurationMs - duration) < TEST_DURATION_THRESHOLD_MS);
         assertTrue(op.getStats().flatten().containsKey("executed"));
 
         op = new FakeSensorOperation(true, 0, TimeUnit.MILLISECONDS);
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -68,7 +77,7 @@
         SensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
 
         long startMs = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long durationMs = System.currentTimeMillis() - startMs;
         long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - durationMs);
         assertTrue(durationDeltaMs < TEST_DURATION_THRESHOLD_MS);
@@ -92,7 +101,7 @@
         assertEquals(0, statsKeys.size());
 
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long durationMs = System.currentTimeMillis() - start;
         long durationDeltaMs = Math.abs(subOpDurationMs - durationMs);
         String message = String.format(
@@ -132,7 +141,7 @@
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -172,7 +181,7 @@
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -203,7 +212,7 @@
         assertEquals(0, statsKeys.size());
 
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long duration = System.currentTimeMillis() - start;
         assertTrue(Math.abs(subOpDurationMs * iterations - duration) < TEST_DURATION_THRESHOLD_MS);
 
@@ -228,8 +237,8 @@
             private SensorStats mFakeStats = new SensorStats();
 
             @Override
-            public void execute() {
-                super.execute();
+            public void execute(ISensorTestNode parent) {
+                super.execute(parent);
                 mExecutedCount++;
 
                 if (failCount == mExecutedCount) {
@@ -255,7 +264,7 @@
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
@@ -292,7 +301,7 @@
         assertEquals(0, statsKeys.size());
 
         long start = System.currentTimeMillis();
-        op.execute();
+        op.execute(mTestNode);
         long duration = System.currentTimeMillis() - start;
         assertTrue(Math.abs(subOpDurationMs * subOpCount - duration) < TEST_DURATION_THRESHOLD_MS);
 
@@ -324,7 +333,7 @@
         assertEquals(0, statsKeys.size());
 
         try {
-            op.execute();
+            op.execute(mTestNode);
             fail("AssertionError expected");
         } catch (AssertionError e) {
             // Expected
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
index 85d189a..847c0f2 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -17,6 +17,7 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import java.util.ArrayList;
 
@@ -47,11 +48,12 @@
      * in one operation, it is thrown and all subsequent operations will not run.
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
+        ISensorTestNode currentNode = asTestNode(parent);
         for (int i = 0; i < mOperations.size(); i++) {
             SensorOperation operation = mOperations.get(i);
             try {
-                operation.execute();
+                operation.execute(currentNode);
             } catch (AssertionError e) {
                 String msg = String.format("Operation %d failed: \"%s\"", i, e.getMessage());
                 getStats().addValue(SensorStats.ERROR, msg);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 7347fc7..3b90b15 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -20,10 +20,12 @@
 
 import android.hardware.cts.helpers.SensorCtsHelper;
 import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.TestSensorEvent;
 import android.hardware.cts.helpers.TestSensorEventListener;
 import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.hardware.cts.helpers.sensorverification.EventGapVerification;
 import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
 import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
@@ -34,20 +36,25 @@
 import android.hardware.cts.helpers.sensorverification.MeanVerification;
 import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
 import android.os.Handler;
+import android.util.Log;
 
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
  * A {@link SensorOperation} used to verify that sensor events and sensor values are correct.
  * <p>
  * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
- * events and then run all the tests.
+ * depending on sensor type.  When {{@link #execute(ISensorTestNode)} is called, the sensor will
+ * collect the events and then run all the tests.
  * </p>
  */
 public class TestSensorOperation extends SensorOperation {
+    private static final String TAG = "TestSensorOperation";
+
     private final HashSet<ISensorVerification> mVerifications = new HashSet<>();
 
     private final TestSensorManager mSensorManager;
@@ -55,8 +62,6 @@
     private final Executor mExecutor;
     private final Handler mHandler;
 
-    private boolean mLogEvents;
-
     /**
      * An interface that defines an abstraction for operations to be performed by the
      * {@link TestSensorOperation}.
@@ -87,13 +92,6 @@
     }
 
     /**
-     * Set whether to log events.
-     */
-    public void setLogEvents(boolean logEvents) {
-        mLogEvents = logEvents;
-    }
-
-    /**
      * Set all of the default test expectations.
      */
     public void addDefaultVerifications() {
@@ -117,10 +115,9 @@
      * Collect the specified number of events from the sensor and run all enabled verifications.
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
         getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
         TestSensorEventListener listener = new TestSensorEventListener(mEnvironment, mHandler);
-
         mExecutor.execute(mSensorManager, listener);
 
         boolean failed = false;
@@ -131,6 +128,8 @@
         }
 
         if (failed) {
+            trySaveCollectedEvents(parent, listener);
+
             String msg = SensorCtsHelper
                     .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
             getStats().addValue(SensorStats.ERROR, msg);
@@ -173,6 +172,34 @@
     }
 
     /**
+     * Tries to save collected {@link TestSensorEvent}s to a file.
+     *
+     * NOTE: it is more important to handle verifications and its results, than failing if the file
+     * cannot be created. So we silently fail if necessary.
+     */
+    private void trySaveCollectedEvents(ISensorTestNode parent, TestSensorEventListener listener) {
+        String sanitizedFileName;
+        try {
+            String fileName = asTestNode(parent).getName();
+            sanitizedFileName = String.format(
+                    "%s-%s-%s_%dus.txt",
+                    SensorCtsHelper.sanitizeStringForFileName(fileName),
+                    SensorStats.getSanitizedSensorName(mEnvironment.getSensor()),
+                    mEnvironment.getFrequencyString(),
+                    mEnvironment.getMaxReportLatencyUs());
+        } catch (SensorTestPlatformException e) {
+            Log.w(TAG, "Unable to generate file name to save collected events", e);
+            return;
+        }
+
+        try {
+            listener.logCollectedEventsToFile(sanitizedFileName);
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to save collected events to file: " + sanitizedFileName, e);
+        }
+    }
+
+    /**
      * Creates an operation that will wait for a given amount of events to arrive.
      *
      * @param environment The test environment.
@@ -186,8 +213,8 @@
             public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
                     throws InterruptedException {
                 try {
-                    sensorManager.registerListener(listener);
-                    listener.waitForEvents(eventCount);
+                    CountDownLatch latch = sensorManager.registerListener(listener, eventCount);
+                    listener.waitForEvents(latch, eventCount);
                 } finally {
                     sensorManager.unregisterListener();
                 }
@@ -241,8 +268,8 @@
                 try {
                     sensorManager.registerListener(listener);
                     SensorCtsHelper.sleep(duration, timeUnit);
-                    sensorManager.requestFlush();
-                    listener.waitForFlushComplete();
+                    CountDownLatch latch = sensorManager.requestFlush();
+                    listener.waitForFlushComplete(latch);
                 } finally {
                     sensorManager.unregisterListener();
                 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
index 5f4f5d8..9f03f31 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -17,7 +17,7 @@
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.content.Context;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 
@@ -40,6 +40,7 @@
      * @param wakeLockFlags the flags used when acquiring the wake-lock
      */
     public WakeLockOperation(SensorOperation operation, Context context, int wakeLockFlags) {
+        super(operation.getStats());
         mOperation = operation;
         mContext = context;
         mWakeLockFlags = wakeLockFlags;
@@ -59,13 +60,12 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() throws InterruptedException {
+    public void execute(ISensorTestNode parent) throws InterruptedException {
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         WakeLock wakeLock = pm.newWakeLock(mWakeLockFlags, TAG);
-
         wakeLock.acquire();
         try {
-            mOperation.execute();
+            mOperation.execute(asTestNode(parent));
         } finally {
             wakeLock.release();
         }
@@ -75,15 +75,7 @@
      * {@inheritDoc}
      */
     @Override
-    public SensorStats getStats() {
-        return mOperation.getStats();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public SensorOperation clone() {
-        return new WakeLockOperation(mOperation, mContext, mWakeLockFlags);
+        return new WakeLockOperation(mOperation.clone(), mContext, mWakeLockFlags);
     }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java
index db6d6ba..7aa6a9d 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyChainTest.java
@@ -16,10 +16,11 @@
 
 package android.keystore.cts;
 
+import android.content.pm.PackageManager;
 import android.security.KeyChain;
-import junit.framework.TestCase;
+import android.test.AndroidTestCase;
 
-public class KeyChainTest extends TestCase {
+public class KeyChainTest extends AndroidTestCase {
     public void testIsKeyAlgorithmSupported_RequiredAlgorithmsSupported() throws Exception {
         assertTrue("DSA must be supported", KeyChain.isKeyAlgorithmSupported("DSA"));
         assertTrue("EC must be supported", KeyChain.isKeyAlgorithmSupported("EC"));
@@ -34,11 +35,21 @@
      * tests in hardware/libhardware/tests/keymaster/
      */
     public void testIsBoundKeyAlgorithm_RequiredAlgorithmsSupported() throws Exception {
-        assertTrue("RSA must be hardware-backed by a hardware-specific Keymaster HAL",
-                KeyChain.isBoundKeyAlgorithm("RSA"));
+        if (isLeanbackOnly()) {
+            KeyChain.isBoundKeyAlgorithm("RSA");
+        }
+        else {
+            assertTrue("RSA must be hardware-backed by a hardware-specific Keymaster HAL",
+                       KeyChain.isBoundKeyAlgorithm("RSA"));
+        }
 
         // These are not required, but must not throw an exception
         KeyChain.isBoundKeyAlgorithm("DSA");
         KeyChain.isBoundKeyAlgorithm("EC");
     }
+
+    private boolean isLeanbackOnly() {
+        PackageManager pm = getContext().getPackageManager();
+        return (pm != null && pm.hasSystemFeature("android.software.leanback_only"));
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
index 7dfb1f6..dd96c2c 100644
--- a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
+++ b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
@@ -59,7 +59,7 @@
         CamcorderProfile.QUALITY_HIGH_SPEED_2160P
     };
     private static final int LAST_QUALITY = CamcorderProfile.QUALITY_2160P;
-    private static final int LAST_TIMELAPSE_QUALITY = CamcorderProfile.QUALITY_TIME_LAPSE_1080P;
+    private static final int LAST_TIMELAPSE_QUALITY = CamcorderProfile.QUALITY_TIME_LAPSE_2160P;
     private static final int LAST_HIGH_SPEED_QUALITY = CamcorderProfile.QUALITY_HIGH_SPEED_2160P;
     private static final Integer[] UNKNOWN_QUALITIES = {
         LAST_QUALITY + 1, // Unknown normal profile quality
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index 5f326ee..96150ca 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -62,7 +62,8 @@
     private static final long WAIT_FOR_IMAGE_TIMEOUT_MS = 1000;
     private static final String DEBUG_FILE_NAME_BASE = "/sdcard/";
     private static final int NUM_FRAME_DECODED = 100;
-    private static final int MAX_NUM_IMAGES = 3;
+    // video decoders only support a single outstanding image with the consumer
+    private static final int MAX_NUM_IMAGES = 1;
 
     private Resources mResources;
     private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index bfe6b3f..e058981 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -254,9 +254,7 @@
             mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
             mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
 
-            assertFalse(mp.isPlaying());
             mp.start();
-            assertTrue(mp.isPlaying());
 
             assertFalse(mp.isLooping());
             mp.setLooping(true);
@@ -270,13 +268,6 @@
             mp.seekTo(pos + seekDuration);
             assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
 
-            // test pause and restart
-            mp.pause();
-            Thread.sleep(SLEEP_TIME);
-            assertFalse(mp.isPlaying());
-            mp.start();
-            assertTrue(mp.isPlaying());
-
             // test stop and restart
             mp.stop();
             mp.reset();
@@ -284,14 +275,9 @@
             mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
             afd.close();
             mp.prepare();
-            assertFalse(mp.isPlaying());
             mp.start();
-            assertTrue(mp.isPlaying());
 
-            // waiting to complete
-            while(mp.isPlaying()) {
-                Thread.sleep(SLEEP_TIME);
-            }
+            Thread.sleep(SLEEP_TIME);
         } finally {
             mp.release();
         }
diff --git a/tests/tests/media/src/android/media/cts/PostProcTestBase.java b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
index c31693e..ef87662 100644
--- a/tests/tests/media/src/android/media/cts/PostProcTestBase.java
+++ b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
@@ -31,6 +31,8 @@
     protected Looper mLooper = null;
     protected final Object mLock = new Object();
     protected int mChangedParameter = -1;
+    protected final static String BUNDLE_VOLUME_EFFECT_UUID =
+            "119341a0-8469-11df-81f9-0002a5d5c51b";
 
     protected boolean hasAudioOutput() {
         return getContext().getPackageManager().hasSystemFeature(
diff --git a/tests/tests/media/src/android/media/cts/PresentationSyncTest.java b/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
index ac86f1e..efa2e0a 100644
--- a/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
@@ -109,7 +109,20 @@
         CpuWaster cpuWaster = new CpuWaster();
         try {
             cpuWaster.start();
-            runThroughputTest(output, refreshNsec, 0.75f);
+            // Tests with mult < 1.0f are flaky, for two reasons:
+            //
+            // (a) They assume that the GPU can render the test scene in less than mult*refreshNsec.
+            //     It's a simple scene, but CTS/CDD don't currently require being able to do more
+            //     than a full-screen clear in refreshNsec.
+            //
+            // (b) More importantly, it assumes that the only rate-limiting happening is
+            //     backpressure from the buffer queue. If the EGL implementation is doing its own
+            //     rate-limiting (to limit the amount of work queued to the GPU at any time), then
+            //     depending on how that's implemented the buffer queue may not have two frames
+            //     pending very often. So the consumer won't be able to drop many frames, and the
+            //     throughput won't be much better than with mult=1.0.
+            //
+            // runThroughputTest(output, refreshNsec, 0.75f);
             cpuWaster.stop();
             runThroughputTest(output, refreshNsec, 1.0f);
             runThroughputTest(output, refreshNsec, 2.0f);
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index b275232..858e47c 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -552,6 +552,7 @@
             implements SurfaceTexture.OnFrameAvailableListener {
         private static final String TAG = "SurfaceVideoProcessor";
         private boolean mFrameAvailable;
+        private boolean mEncoderIsActive;
         private boolean mGotDecoderEOS;
         private boolean mSignaledEncoderEOS;
 
@@ -594,7 +595,7 @@
                         try {
                             // wait for mFrameAvailable, which is set by onFrameAvailable().
                             // Use a timeout to avoid stalling the test if it doesn't arrive.
-                            if (!mFrameAvailable && !mCompleted) {
+                            if (!mFrameAvailable && !mCompleted && !mEncoderIsActive) {
                                 mCondition.wait(FRAME_TIMEOUT_MS);
                             }
                         } catch (InterruptedException ie) {
@@ -603,6 +604,11 @@
                         if (mCompleted) {
                             break;
                         }
+                        if (mEncoderIsActive) {
+                            mEncoderIsActive = false;
+                            if (DEBUG) Log.d(TAG, "encoder is still active, continue");
+                            continue;
+                        }
                         assertTrue("still waiting for image", mFrameAvailable);
                         if (DEBUG) Log.v(TAG, "got image");
                         info = mInfoOnSurface;
@@ -704,6 +710,12 @@
                 }
             } else if (mediaCodec == mEncoder) {
                 emptyEncoderOutputBuffer(ix, info);
+                synchronized(mCondition) {
+                    if (!mCompleted) {
+                        mEncoderIsActive = true;
+                        mCondition.notifyAll();
+                    }
+                }
             } else {
                 fail("received output buffer on " + mediaCodec.getName());
             }
@@ -746,7 +758,8 @@
 
         final private Map<Size, Set<Size>> mMinMax;     // extreme sizes
         final private Map<Size, Set<Size>> mNearMinMax; // sizes near extreme
-        final private Set<Size> mArbitrary;             // arbitrary sizes in the middle
+        final private Set<Size> mArbitraryW;            // arbitrary widths in the middle
+        final private Set<Size> mArbitraryH;            // arbitrary heights in the middle
         final private Set<Size> mSizes;                 // all non-specifically tested sizes
 
         final private int xAlign;
@@ -760,7 +773,8 @@
             /* calculate min/max sizes */
             mMinMax = new HashMap<Size, Set<Size>>();
             mNearMinMax = new HashMap<Size, Set<Size>>();
-            mArbitrary = new HashSet<Size>();
+            mArbitraryW = new HashSet<Size>();
+            mArbitraryH = new HashSet<Size>();
             mSizes = new HashSet<Size>();
 
             xAlign = mCaps.getWidthAlignment();
@@ -779,18 +793,28 @@
             // initialize arbitrary sizes
             for (int i = 1; i <= 7; ++i) {
                 int j = ((7 * i) % 11) + 1;
-                int width = alignedPointInRange(i * 0.125, xAlign, mCaps.getSupportedWidths());
-                int height = alignedPointInRange(
-                        j * 0.077, yAlign, mCaps.getSupportedHeightsFor(width));
-                mArbitrary.add(new Size(width, height));
+                int width, height;
+                try {
+                    width = alignedPointInRange(i * 0.125, xAlign, mCaps.getSupportedWidths());
+                    height = alignedPointInRange(
+                            j * 0.077, yAlign, mCaps.getSupportedHeightsFor(width));
+                    mArbitraryW.add(new Size(width, height));
+                } catch (IllegalArgumentException e) {
+                }
 
-                height = alignedPointInRange(i * 0.125, yAlign, mCaps.getSupportedHeights());
-                width = alignedPointInRange(j * 0.077, xAlign, mCaps.getSupportedWidthsFor(height));
-                mArbitrary.add(new Size(width, height));
+                try {
+                    height = alignedPointInRange(i * 0.125, yAlign, mCaps.getSupportedHeights());
+                    width = alignedPointInRange(j * 0.077, xAlign, mCaps.getSupportedWidthsFor(height));
+                    mArbitraryH.add(new Size(width, height));
+                } catch (IllegalArgumentException e) {
+                }
             }
-            mArbitrary.removeAll(mSizes);
-            mSizes.addAll(mArbitrary);
-            if (DEBUG) Log.i(TAG, "arbitrary=" + mArbitrary);
+            mArbitraryW.removeAll(mArbitraryH);
+            mArbitraryW.removeAll(mSizes);
+            mSizes.addAll(mArbitraryW);
+            mArbitraryH.removeAll(mSizes);
+            mSizes.addAll(mArbitraryH);
+            if (DEBUG) Log.i(TAG, "arbitrary=" + mArbitraryW + "/" + mArbitraryH);
         }
 
         private void addExtremeSizesFor(int x, int y) {
@@ -892,9 +916,9 @@
             return !skipped;
         }
 
-        public boolean testArbitrary(boolean flexYUV) {
+        public boolean testArbitrary(boolean flexYUV, boolean widths) {
             boolean skipped = true;
-            for (Size s : mArbitrary) {
+            for (Size s : (widths ? mArbitraryW : mArbitraryH)) {
                 if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
                     skipped = false;
                 }
@@ -990,11 +1014,14 @@
                     || info.getName().toLowerCase().startsWith("omx.google.") != goog) {
                 continue;
             }
+            CodecCapabilities caps = null;
             try {
-                CodecCapabilities caps = info.getCapabilitiesForType(mime);
-                result.add(new Encoder(info.getName(), mime, caps));
+                caps = info.getCapabilitiesForType(mime);
             } catch (IllegalArgumentException e) { // mime is not supported
+                continue;
             }
+            assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
+            result.add(new Encoder(info.getName(), mime, caps));
         }
         return result.toArray(new Encoder[result.size()]);
     }
@@ -1207,31 +1234,57 @@
     public void testOtherVP9FlexNearMaxMax()   { nearmaxmax(otherVP9(),   true /* flex */); }
     public void testOtherVP9SurfNearMaxMax()   { nearmaxmax(otherVP9(),   false /* flex */); }
 
-    public void testGoogH265FlexArbitrary()   { arbitrary(googH265(),   true /* flex */); }
-    public void testGoogH265SurfArbitrary()   { arbitrary(googH265(),   false /* flex */); }
-    public void testGoogH264FlexArbitrary()   { arbitrary(googH264(),   true /* flex */); }
-    public void testGoogH264SurfArbitrary()   { arbitrary(googH264(),   false /* flex */); }
-    public void testGoogH263FlexArbitrary()   { arbitrary(googH263(),   true /* flex */); }
-    public void testGoogH263SurfArbitrary()   { arbitrary(googH263(),   false /* flex */); }
-    public void testGoogMpeg4FlexArbitrary()  { arbitrary(googMpeg4(),  true /* flex */); }
-    public void testGoogMpeg4SurfArbitrary()  { arbitrary(googMpeg4(),  false /* flex */); }
-    public void testGoogVP8FlexArbitrary()    { arbitrary(googVP8(),    true /* flex */); }
-    public void testGoogVP8SurfArbitrary()    { arbitrary(googVP8(),    false /* flex */); }
-    public void testGoogVP9FlexArbitrary()    { arbitrary(googVP9(),    true /* flex */); }
-    public void testGoogVP9SurfArbitrary()    { arbitrary(googVP9(),    false /* flex */); }
+    public void testGoogH265FlexArbitraryW()   { arbitraryw(googH265(),   true /* flex */); }
+    public void testGoogH265SurfArbitraryW()   { arbitraryw(googH265(),   false /* flex */); }
+    public void testGoogH264FlexArbitraryW()   { arbitraryw(googH264(),   true /* flex */); }
+    public void testGoogH264SurfArbitraryW()   { arbitraryw(googH264(),   false /* flex */); }
+    public void testGoogH263FlexArbitraryW()   { arbitraryw(googH263(),   true /* flex */); }
+    public void testGoogH263SurfArbitraryW()   { arbitraryw(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexArbitraryW()  { arbitraryw(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfArbitraryW()  { arbitraryw(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexArbitraryW()    { arbitraryw(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfArbitraryW()    { arbitraryw(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexArbitraryW()    { arbitraryw(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfArbitraryW()    { arbitraryw(googVP9(),    false /* flex */); }
 
-    public void testOtherH265FlexArbitrary()  { arbitrary(otherH265(),  true /* flex */); }
-    public void testOtherH265SurfArbitrary()  { arbitrary(otherH265(),  false /* flex */); }
-    public void testOtherH264FlexArbitrary()  { arbitrary(otherH264(),  true /* flex */); }
-    public void testOtherH264SurfArbitrary()  { arbitrary(otherH264(),  false /* flex */); }
-    public void testOtherH263FlexArbitrary()  { arbitrary(otherH263(),  true /* flex */); }
-    public void testOtherH263SurfArbitrary()  { arbitrary(otherH263(),  false /* flex */); }
-    public void testOtherMpeg4FlexArbitrary() { arbitrary(otherMpeg4(), true /* flex */); }
-    public void testOtherMpeg4SurfArbitrary() { arbitrary(otherMpeg4(), false /* flex */); }
-    public void testOtherVP8FlexArbitrary()   { arbitrary(otherVP8(),   true /* flex */); }
-    public void testOtherVP8SurfArbitrary()   { arbitrary(otherVP8(),   false /* flex */); }
-    public void testOtherVP9FlexArbitrary()   { arbitrary(otherVP9(),   true /* flex */); }
-    public void testOtherVP9SurfArbitrary()   { arbitrary(otherVP9(),   false /* flex */); }
+    public void testOtherH265FlexArbitraryW()  { arbitraryw(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfArbitraryW()  { arbitraryw(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexArbitraryW()  { arbitraryw(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfArbitraryW()  { arbitraryw(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexArbitraryW()  { arbitraryw(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfArbitraryW()  { arbitraryw(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexArbitraryW() { arbitraryw(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfArbitraryW() { arbitraryw(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexArbitraryW()   { arbitraryw(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfArbitraryW()   { arbitraryw(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexArbitraryW()   { arbitraryw(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfArbitraryW()   { arbitraryw(otherVP9(),   false /* flex */); }
+
+    public void testGoogH265FlexArbitraryH()   { arbitraryh(googH265(),   true /* flex */); }
+    public void testGoogH265SurfArbitraryH()   { arbitraryh(googH265(),   false /* flex */); }
+    public void testGoogH264FlexArbitraryH()   { arbitraryh(googH264(),   true /* flex */); }
+    public void testGoogH264SurfArbitraryH()   { arbitraryh(googH264(),   false /* flex */); }
+    public void testGoogH263FlexArbitraryH()   { arbitraryh(googH263(),   true /* flex */); }
+    public void testGoogH263SurfArbitraryH()   { arbitraryh(googH263(),   false /* flex */); }
+    public void testGoogMpeg4FlexArbitraryH()  { arbitraryh(googMpeg4(),  true /* flex */); }
+    public void testGoogMpeg4SurfArbitraryH()  { arbitraryh(googMpeg4(),  false /* flex */); }
+    public void testGoogVP8FlexArbitraryH()    { arbitraryh(googVP8(),    true /* flex */); }
+    public void testGoogVP8SurfArbitraryH()    { arbitraryh(googVP8(),    false /* flex */); }
+    public void testGoogVP9FlexArbitraryH()    { arbitraryh(googVP9(),    true /* flex */); }
+    public void testGoogVP9SurfArbitraryH()    { arbitraryh(googVP9(),    false /* flex */); }
+
+    public void testOtherH265FlexArbitraryH()  { arbitraryh(otherH265(),  true /* flex */); }
+    public void testOtherH265SurfArbitraryH()  { arbitraryh(otherH265(),  false /* flex */); }
+    public void testOtherH264FlexArbitraryH()  { arbitraryh(otherH264(),  true /* flex */); }
+    public void testOtherH264SurfArbitraryH()  { arbitraryh(otherH264(),  false /* flex */); }
+    public void testOtherH263FlexArbitraryH()  { arbitraryh(otherH263(),  true /* flex */); }
+    public void testOtherH263SurfArbitraryH()  { arbitraryh(otherH263(),  false /* flex */); }
+    public void testOtherMpeg4FlexArbitraryH() { arbitraryh(otherMpeg4(), true /* flex */); }
+    public void testOtherMpeg4SurfArbitraryH() { arbitraryh(otherMpeg4(), false /* flex */); }
+    public void testOtherVP8FlexArbitraryH()   { arbitraryh(otherVP8(),   true /* flex */); }
+    public void testOtherVP8SurfArbitraryH()   { arbitraryh(otherVP8(),   false /* flex */); }
+    public void testOtherVP9FlexArbitraryH()   { arbitraryh(otherVP9(),   true /* flex */); }
+    public void testOtherVP9SurfArbitraryH()   { arbitraryh(otherVP9(),   false /* flex */); }
 
     public void testGoogH265FlexQCIF()   { specific(googH265(),   176, 144, true /* flex */); }
     public void testGoogH265SurfQCIF()   { specific(googH265(),   176, 144, false /* flex */); }
@@ -1471,14 +1524,14 @@
         }
     }
 
-    private void arbitrary(Encoder[] encoders, boolean flexYUV) {
+    private void arbitrary(Encoder[] encoders, boolean flexYUV, boolean widths) {
         boolean skipped = true;
         if (encoders.length == 0) {
             MediaUtils.skipTest("no such encoder present");
             return;
         }
         for (Encoder encoder: encoders) {
-            if (encoder.testArbitrary(flexYUV)) {
+            if (encoder.testArbitrary(flexYUV, widths)) {
                 skipped = false;
             }
         }
@@ -1487,6 +1540,14 @@
         }
     }
 
+    private void arbitraryw(Encoder[] encoders, boolean flexYUV) {
+        arbitrary(encoders, flexYUV, true /* widths */);
+    }
+
+    private void arbitraryh(Encoder[] encoders, boolean flexYUV) {
+        arbitrary(encoders, flexYUV, false /* widths */);
+    }
+
     /* test specific size */
     private void specific(Encoder[] encoders, int width, int height, boolean flexYUV) {
         boolean skipped = true;
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index 73e48f2..ea7a13f 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -27,6 +27,7 @@
 import android.media.audiofx.Visualizer.MeasurementPeakRms;
 import android.os.Looper;
 import android.test.AndroidTestCase;
+import java.util.UUID;
 import android.util.Log;
 
 public class VisualizerTest extends PostProcTestBase {
@@ -281,6 +282,7 @@
 
     //Test case 4.1: test measurement of peak / RMS
     public void test4_1MeasurePeakRms() throws Exception {
+        AudioEffect vc = null;
         try {
             // this test will play a 1kHz sine wave with peaks at -40dB
             MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
@@ -289,6 +291,15 @@
             final int MAX_MEASUREMENT_ERROR_MB = 2000;
             assertNotNull("null MediaPlayer", mp);
 
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+                    0,
+                    mp.getAudioSessionId());
+            vc.setEnabled(true);
+
             AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
             assertNotNull("null AudioManager", am);
             int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -308,7 +319,7 @@
             int currentPosition = mp.getCurrentPosition();
             final int maxTry = 100;
             int tryCount = 0;
-            while (currentPosition < 400 && tryCount < maxTry) {
+            while (currentPosition < 200 && tryCount < maxTry) {
                 Thread.sleep(50);
                 currentPosition = mp.getCurrentPosition();
                 tryCount++;
@@ -333,12 +344,15 @@
         } catch (InterruptedException e) {
             fail("sleep() interrupted");
         } finally {
+            if (vc != null)
+                vc.release();
             releaseVisualizer();
         }
     }
 
     //Test case 4.2: test measurement of peak / RMS in Long MP3
     public void test4_2MeasurePeakRmsLongMP3() throws Exception {
+        AudioEffect vc = null;
         try {
             // this test will play a 1kHz sine wave with peaks at -40dB
             MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong);
@@ -347,6 +361,15 @@
             final int MAX_MEASUREMENT_ERROR_MB = 2000;
             assertNotNull("null MediaPlayer", mp);
 
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+                    0,
+                    mp.getAudioSessionId());
+            vc.setEnabled(true);
+
             AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
             assertNotNull("null AudioManager", am);
             int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -390,6 +413,8 @@
         } catch (InterruptedException e) {
             fail("sleep() interrupted");
         } finally {
+            if (vc != null)
+                vc.release();
             releaseVisualizer();
         }
     }
diff --git a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
index e9bfb43..eddb416 100644
--- a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
@@ -53,6 +53,9 @@
     /** Maximum size of the packets we're using to test. */
     private static final int MAX_SIZE = 4096;
 
+    /** Size of the ICMPv6 header. */
+    private static final int ICMP_HEADER_SIZE = 8;
+
     /** Number of packets to test. */
     private static final int NUM_PACKETS = 10;
 
@@ -65,7 +68,7 @@
      * Returns a byte array containing an ICMPv6 echo request with the specified payload length.
      */
     private byte[] pingPacket(int payloadLength) {
-        byte[] packet = new byte[payloadLength + 8];
+        byte[] packet = new byte[payloadLength + ICMP_HEADER_SIZE];
         new Random().nextBytes(packet);
         System.arraycopy(PING_HEADER, 0, packet, 0, PING_HEADER.length);
         return packet;
@@ -155,7 +158,7 @@
         assertEquals("localhost/::1", ipv6Loopback.toString());
 
         for (int i = 0; i < NUM_PACKETS; i++) {
-            byte[] packet = pingPacket((int) (Math.random() * MAX_SIZE));
+            byte[] packet = pingPacket((int) (Math.random() * (MAX_SIZE - ICMP_HEADER_SIZE)));
             FileDescriptor s = createPingSocket();
             // Use both recvfrom and read().
             sendPing(s, ipv6Loopback, packet);
diff --git a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
index 7b1d84c..ea0e80a 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -228,14 +228,14 @@
     }
 
     /**
-     * Tests the TelephonyManager.setGlobalPreferredNetworkType() API. This makes a call to
-     * setGlobalPreferredNetworkType() API and expects a SecurityException since the test apk is not
+     * Tests the TelephonyManager.setPreferredNetworkTypeToGlobal() API. This makes a call to
+     * setPreferredNetworkTypeToGlobal() API and expects a SecurityException since the test apk is not
      * signed by certificate on the SIM.
      */
-    public void testSetGlobalPreferredNetworkType() {
+    public void testSetPreferredNetworkTypeToGlobal() {
         try {
             if (isSimCardPresent()) {
-                TelephonyManager.getDefault().setGlobalPreferredNetworkType();
+                TelephonyManager.getDefault().setPreferredNetworkTypeToGlobal();
                 fail("Expected SecurityException. App doesn't have carrier privileges.");
             }
         } catch (SecurityException expected) {
diff --git a/tests/tests/uirendering/res/layout/blue_padded_layout.xml b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
index 68c9cd1..2bfd049 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_layout.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
@@ -14,16 +14,15 @@
        limitations under the License.
   -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/test_root"
-        android:layout_width="200px"
-        android:layout_height="200px"
-        android:clipChildren="false">
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"
+    android:clipChildren="false">
     <android.uirendering.cts.testclasses.view.UnclippedBlueView
-            android:id="@+id/child"
-            android:layout_width="100px"
-            android:layout_height="100px"
-            android:paddingLeft="15px"
-            android:paddingTop="16px"
-            android:paddingRight="17px"
-            android:paddingBottom="18px"/>
+        android:id="@+id/child"
+        android:layout_width="80px"
+        android:layout_height="80px"
+        android:paddingLeft="15px"
+        android:paddingTop="16px"
+        android:paddingRight="17px"
+        android:paddingBottom="18px"/>
 </FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/blue_padded_square.xml b/tests/tests/uirendering/res/layout/blue_padded_square.xml
index 71f4b0c..7d867d9 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_square.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_square.xml
@@ -14,7 +14,6 @@
        limitations under the License.
   -->
 <View xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/test_root"
-    android:layout_width="100px"
-    android:layout_height="100px"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"
     android:background="@drawable/blue_padded_square"/>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/tests/tests/uirendering/res/layout/draw_activity_view.xml
deleted file mode 100644
index 54a72e3..0000000
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/tests/tests/uirendering/res/layout/simple_rect_layout.xml b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
index b570df8..7d6f928 100644
--- a/tests/tests/uirendering/res/layout/simple_rect_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
@@ -13,14 +13,12 @@
        See the License for the specific language governing permissions and
        limitations under the License.
   -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/test_root"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <View android:layout_width="100px"
-        android:layout_height="100px"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height">
+    <View android:layout_width="80px"
+        android:layout_height="80px"
+        android:translationX="5px"
+        android:translationY="5px"
         android:background="#00f" />
-
-</LinearLayout>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/simple_red_layout.xml b/tests/tests/uirendering/res/layout/simple_red_layout.xml
index 2d2d189..2af8db6 100644
--- a/tests/tests/uirendering/res/layout/simple_red_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_red_layout.xml
@@ -14,7 +14,7 @@
        limitations under the License.
   -->
 <View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"
     android:id="@+id/test_root"
-    android:layout_width="180px"
-    android:layout_height="180px"
     android:background="#f00" />
diff --git a/tests/tests/uirendering/res/layout/test_container.xml b/tests/tests/uirendering/res/layout/test_container.xml
new file mode 100644
index 0000000..94a8eab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_container.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+       Licensed under the Apache License, Version 2.0 (the "License");
+       you may not use this file except in compliance with the License.
+       You may obtain a copy of the License at
+
+            http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+       See the License for the specific language governing permissions and
+       limitations under the License.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/test_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ViewStub
+        android:id="@+id/test_content_stub"
+        android:layout_gravity="center"
+        android:layout_width="@dimen/test_width"
+        android:layout_height="@dimen/test_height"/>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
new file mode 100644
index 0000000..9f8a139
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
@@ -0,0 +1,18 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+       Licensed under the Apache License, Version 2.0 (the "License");
+       you may not use this file except in compliance with the License.
+       You may obtain a copy of the License at
+
+            http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+       See the License for the specific language governing permissions and
+       limitations under the License.
+  -->
+<android.uirendering.cts.testinfrastructure.CanvasClientView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/layout/test_content_webview.xml b/tests/tests/uirendering/res/layout/test_content_webview.xml
new file mode 100644
index 0000000..8b03cab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_webview.xml
@@ -0,0 +1,18 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+       Licensed under the Apache License, Version 2.0 (the "License");
+       you may not use this file except in compliance with the License.
+       You may obtain a copy of the License at
+
+            http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+       See the License for the specific language governing permissions and
+       limitations under the License.
+  -->
+<WebView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/values/dimens.xml b/tests/tests/uirendering/res/values/dimens.xml
new file mode 100644
index 0000000..1c304d3
--- /dev/null
+++ b/tests/tests/uirendering/res/values/dimens.xml
@@ -0,0 +1,18 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+       Licensed under the Apache License, Version 2.0 (the "License");
+       you may not use this file except in compliance with the License.
+       You may obtain a copy of the License at
+
+            http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+       See the License for the specific language governing permissions and
+       limitations under the License.
+  -->
+<resources>
+    <dimen name="test_width">90px</dimen>
+    <dimen name="test_height">90px</dimen>
+</resources>
diff --git a/tests/tests/uirendering/res/values/themes.xml b/tests/tests/uirendering/res/values/themes.xml
index 751b7cb..f08f029 100644
--- a/tests/tests/uirendering/res/values/themes.xml
+++ b/tests/tests/uirendering/res/values/themes.xml
@@ -17,9 +17,9 @@
     <style name="WhiteBackgroundTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
         <item name="android:fadingEdge">none</item>
         <item name="android:windowBackground">@android:color/white</item>
-        <!--This shouldn't be necessary currently a hack for an existing bug with transitions-->
         <item name="android:windowContentTransitions">false</item>
         <item name="android:windowAnimationStyle">@null</item>
     </style>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
index ea836ea..7f62b3e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
@@ -22,11 +22,16 @@
     private int mColor;
 
     public ColorVerifier(int color) {
-        this(color, 20);
+        this(color, DEFAULT_THRESHOLD);
     }
 
-    public ColorVerifier(int color, int threshold) {
-        super(threshold);
+    public ColorVerifier(int color, int colorTolerance) {
+        super(colorTolerance);
+        mColor = color;
+    }
+
+    public ColorVerifier(int color, int colorThreshold, float spatialTolerance) {
+        super(colorThreshold, spatialTolerance);
         mColor = color;
     }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
index 672b3f6..d4a63de 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -18,6 +18,7 @@
 import android.graphics.Bitmap;
 import android.uirendering.cts.bitmapcomparers.BitmapComparer;
 import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 
 public class GoldenImageVerifier extends BitmapVerifier {
     private BitmapComparer mBitmapComparer;
@@ -26,7 +27,7 @@
     public GoldenImageVerifier(Bitmap goldenBitmap, BitmapComparer bitmapComparer) {
         mGoldenBitmapArray = new int[goldenBitmap.getWidth() * goldenBitmap.getHeight()];
         goldenBitmap.getPixels(mGoldenBitmapArray, 0, goldenBitmap.getWidth(), 0, 0,
-                goldenBitmap.getWidth(), goldenBitmap.getHeight());
+                ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
         mBitmapComparer = bitmapComparer;
     }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
index 0bdcc9b..2d00db5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -26,38 +26,54 @@
  */
 public abstract class PerPixelBitmapVerifier extends BitmapVerifier {
     private static final String TAG = "PerPixelBitmapVerifer";
-    private int mTolerance;
+    public static final int DEFAULT_THRESHOLD = 48;
 
-    public PerPixelBitmapVerifier(int tolerance) {
-        mTolerance = tolerance;
+    // total color difference tolerated without the pixel failing
+    private int mColorTolerance;
+
+    // portion of bitmap allowed to fail pixel check
+    private float mSpatialTolerance;
+
+    public PerPixelBitmapVerifier() {
+        this(DEFAULT_THRESHOLD, 0);
+    }
+
+    public PerPixelBitmapVerifier(int colorTolerance) {
+        this(colorTolerance, 0);
+    }
+
+    public PerPixelBitmapVerifier(int colorTolerance, float spatialTolerance) {
+        mColorTolerance = colorTolerance;
+        mSpatialTolerance = spatialTolerance;
     }
 
     protected int getExpectedColor(int x, int y) {
         return Color.WHITE;
     }
 
-
     public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
-        int failCount = 0;
+        int failures = 0;
         int[] differenceMap = new int[bitmap.length];
         for (int y = 0 ; y < height ; y++) {
             for (int x = 0 ; x < width ; x++) {
                 int index = indexFromXAndY(x, y, stride, offset);
-                int expectedColor = getExpectedColor(x, y);
-                if (!verifyPixel(bitmap[index], expectedColor)) {
-                    if (failCount < 50) {
-                        Log.d(TAG, "Expected : " + Integer.toHexString(expectedColor)
+                if (!verifyPixel(x, y, bitmap[index])) {
+                    if (failures < 50) {
+                        Log.d(TAG, "Expected : " + Integer.toHexString(getExpectedColor(x, y))
                                 + " received : " + Integer.toHexString(bitmap[index])
                                 + " at position (" + x + "," + y + ")");
                     }
-                    failCount++;
+                    failures++;
                     differenceMap[index] = FAIL_COLOR;
                 } else {
                     differenceMap[index] = PASS_COLOR;
                 }
             }
         }
-        boolean success = failCount == 0;
+        int toleratedFailures = (int) (mSpatialTolerance * width * height);
+        boolean success = failures <= toleratedFailures;
+        Log.d(TAG, failures + " failures observed out of "
+                + toleratedFailures + " tolerated failures");
         if (!success) {
             mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
                     ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
@@ -67,7 +83,9 @@
         return success;
     }
 
-    protected boolean verifyPixel(int color, int expectedColor) {
-        return CompareUtils.verifyPixelWithThreshold(color, expectedColor, mTolerance);
+
+    protected boolean verifyPixel(int x, int y, int observedColor) {
+        int expectedColor = getExpectedColor(x, y);
+        return CompareUtils.verifyPixelWithThreshold(observedColor, expectedColor, mColorTolerance);
     }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
index 06a430b..f4bece1 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
@@ -26,7 +26,7 @@
     private Rect mInnerRect;
 
     public RectVerifier(int outerColor, int innerColor, Rect innerRect) {
-        this(outerColor, innerColor, innerRect, 20);
+        this(outerColor, innerColor, innerRect, DEFAULT_THRESHOLD);
     }
 
     public RectVerifier(int outerColor, int innerColor, Rect innerRect, int tolerance) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
index ddae100..117fe17 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
@@ -28,7 +28,6 @@
 import android.uirendering.cts.testinfrastructure.CanvasClient;
 
 public class BitmapFilterTests extends ActivityTestBase {
-    private static final int THRESHOLD = 20;
     private static final int WHITE_WEIGHT = 255 * 3;
     private enum FilterEnum {
         // Creates Paint object that will have bitmap filtering
@@ -40,72 +39,74 @@
         ADD_FILTER
     }
 
-    /**
-     * Verifies that a Bitmap only contains white and black, within a certain threshold
-     */
-    private static BitmapVerifier mBlackWhiteVerifier = new PerPixelBitmapVerifier(THRESHOLD) {
+    private static final BitmapVerifier BLACK_WHITE_ONLY_VERIFIER
+            = new PerPixelBitmapVerifier(PerPixelBitmapVerifier.DEFAULT_THRESHOLD, 0.99f) {
         @Override
-        protected boolean verifyPixel(int color, int expectedColor) {
+        protected boolean verifyPixel(int x, int y, int color) {
             int weight = Color.red(color) + Color.blue(color) + Color.green(color);
-            return weight < THRESHOLD // is approx Color.BLACK
-                    || weight > WHITE_WEIGHT - THRESHOLD; // is approx Color.WHITE
+            return weight < DEFAULT_THRESHOLD // is approx Color.BLACK
+                    || weight > WHITE_WEIGHT - DEFAULT_THRESHOLD; // is approx Color.WHITE
         }
     };
+    private static final BitmapVerifier GREY_ONLY_VERIFIER
+            = new ColorVerifier(Color.argb(255, 127, 127, 127),
+            PerPixelBitmapVerifier.DEFAULT_THRESHOLD);
+    private static final BitmapVerifier GREY_PARTIAL_VERIFIER
+            = new ColorVerifier(Color.argb(255, 127, 127, 127),
+            300, 0.8f); // content will be mostly grey, for a wide range of grey
+
 
     private static Bitmap createGridBitmap(int width, int height) {
         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         for (int i = 0 ; i < width ; i++) {
             for (int j = 0 ; j < height ; j++) {
-                bitmap.setPixel(i, j, ((i + j * width)) % 2 == 0 ?
-                        Color.WHITE : Color.BLACK);
+                boolean isWhite = (i + j * width) % 2 == 0;
+                bitmap.setPixel(i, j, isWhite ? Color.WHITE : Color.BLACK);
             }
         }
         return bitmap;
     }
 
     private static final int SMALL_GRID_SIZE = 5;
-    private static Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
+    private Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
     private static final int BIG_GRID_SIZE = 360;
-    private static Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
-    private static final int HALFWAY_COLOR = Color.argb(255, 127, 127, 127);
+    private Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
 
-    /* All of these tests seem to be broken.
-     * TODO: fix in L MR1
     @SmallTest
     public void testPaintFilterScaleUp() {
-        runScaleTest(FilterEnum.PAINT_FILTER, true, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.PAINT_FILTER, true);
     }
 
     @SmallTest
     public void testPaintFilterScaleDown() {
-        runScaleTest(FilterEnum.PAINT_FILTER, false, new ColorVerifier(HALFWAY_COLOR, 15));
+        runScaleTest(FilterEnum.PAINT_FILTER, false);
     }
 
     @SmallTest
     public void testDrawFilterRemoveFilterScaleUp() {
-        runScaleTest(FilterEnum.REMOVE_FILTER, true, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.REMOVE_FILTER, true);
     }
 
     @SmallTest
     public void testDrawFilterRemoveFilterScaleDown() {
-        runScaleTest(FilterEnum.REMOVE_FILTER, false, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.REMOVE_FILTER, false);
     }
 
     @SmallTest
     public void testDrawFilterScaleUp() {
-        runScaleTest(FilterEnum.ADD_FILTER, true, mBlackWhiteVerifier);
+        runScaleTest(FilterEnum.ADD_FILTER, true);
     }
 
     @SmallTest
     public void testDrawFilterScaleDown() {
-        runScaleTest(FilterEnum.ADD_FILTER, false, new ColorVerifier(HALFWAY_COLOR));
+        runScaleTest(FilterEnum.ADD_FILTER, false);
     }
-*/
-    private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp,
-            BitmapVerifier bitmapVerifier) {
+
+    private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp) {
         final int gridWidth = scaleUp ? SMALL_GRID_SIZE : BIG_GRID_SIZE;
         final Paint paint = new Paint(filterEnum.equals(FilterEnum.ADD_FILTER) ?
                 0 : Paint.FILTER_BITMAP_FLAG);
+
         CanvasClient canvasClient = new CanvasClient() {
             @Override
             public void draw(Canvas canvas, int width, int height) {
@@ -120,6 +121,16 @@
         };
         createTest()
                 .addCanvasClient(canvasClient)
-                .runWithVerifier(bitmapVerifier);
+                .runWithVerifier(getVerifierForTest(filterEnum, scaleUp));
+    }
+
+    private static BitmapVerifier getVerifierForTest(FilterEnum filterEnum, boolean scaleUp) {
+        if (filterEnum.equals(FilterEnum.REMOVE_FILTER)) {
+            // filtering disabled, so only black and white pixels will come through
+            return BLACK_WHITE_ONLY_VERIFIER;
+        }
+        // if scaling up, output pixels may have single source to sample from,
+        // will only be *mostly* grey.
+        return scaleUp ? GREY_PARTIAL_VERIFIER : GREY_ONLY_VERIFIER;
     }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index afbad65..29755d8 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -36,7 +36,7 @@
 
     @SmallTest
     public void testBlueRect() {
-        final Rect rect = new Rect(10, 10, 100, 100);
+        final Rect rect = new Rect(10, 10, 80, 80);
         createTest()
                 .addCanvasClient(new CanvasClient() {
                     @Override
@@ -58,8 +58,6 @@
                     public void draw(Canvas canvas, int width, int height) {
                         Paint p = new Paint();
                         p.setAntiAlias(false);
-                        p.setColor(Color.WHITE);
-                        canvas.drawRect(0, 0, 100, 100, p);
                         p.setStrokeWidth(1f);
                         p.setColor(Color.BLACK);
                         for (int i = 0; i < 10; i++) {
@@ -81,8 +79,8 @@
                         canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
                                 ActivityTestBase.TEST_HEIGHT, p);
                         p.setColor(Color.BLACK);
-                        p.setStrokeWidth(10);
-                        canvas.drawRect(10, 10, 20, 20, p);
+                        p.setStrokeWidth(5);
+                        canvas.drawRect(10, 10, 80, 80, p);
                     }
                 })
                 .runWithComparer(mExactComparer);
@@ -94,10 +92,8 @@
                 .addCanvasClient(new CanvasClient() {
                     @Override
                     public void draw(Canvas canvas, int width, int height) {
+                        canvas.drawColor(Color.GREEN);
                         Paint p = new Paint();
-                        p.setColor(Color.GREEN);
-                        canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
-                                ActivityTestBase.TEST_HEIGHT, p);
                         p.setColor(Color.BLACK);
                         p.setStrokeWidth(10);
                         canvas.drawLine(0, 0, 50, 0, p);
@@ -131,7 +127,7 @@
                         canvas.drawColor(Color.WHITE);
                         p.setColor(Color.BLACK);
                         float[] pts = {
-                                0, 0, 100, 100, 100, 0, 0, 100, 50, 50, 75, 75
+                                0, 0, 80, 80, 80, 0, 0, 80, 40, 50, 60, 50
                         };
                         canvas.drawLines(pts, p);
                     }
@@ -185,10 +181,10 @@
     public void testBluePaddedSquare() {
         final NinePatchDrawable ninePatchDrawable = (NinePatchDrawable)
             getActivity().getResources().getDrawable(R.drawable.blue_padded_square);
-        ninePatchDrawable.setBounds(0, 0, 100, 100);
+        ninePatchDrawable.setBounds(0, 0, 90, 90);
 
         BitmapVerifier verifier = new RectVerifier(Color.WHITE, Color.BLUE,
-                new Rect(10, 10, 90, 90));
+                new Rect(10, 10, 80, 80));
 
         createTest()
                 .addCanvasClient(new CanvasClient() {
@@ -197,7 +193,7 @@
                         canvas.drawColor(Color.WHITE);
                         Paint p = new Paint();
                         p.setColor(Color.BLUE);
-                        canvas.drawRect(10, 10, 90, 90, p);
+                        canvas.drawRect(10, 10, 80, 80, p);
                     }
                 })
                 .addCanvasClient(new CanvasClient() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
index 6662226..c17e106 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
@@ -17,7 +17,6 @@
 
 import com.android.cts.uirendering.R;
 
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
@@ -29,7 +28,6 @@
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 import android.uirendering.cts.testinfrastructure.CanvasClient;
 import android.uirendering.cts.testinfrastructure.ViewInitializer;
-import android.util.Log;
 import android.view.View;
 
 public class InfrastructureTests extends ActivityTestBase {
@@ -52,16 +50,17 @@
         CanvasClient canvasClient = new CanvasClient() {
             @Override
             public void draw(Canvas canvas, int width, int height) {
-                canvas.drawColor(canvas.isHardwareAccelerated() ? Color.WHITE : Color.BLACK);
+                canvas.drawColor(canvas.isHardwareAccelerated() ? Color.BLACK : Color.WHITE);
             }
         };
-        // This is considered a very high threshold and as such, the test should still fail because
-        // they are completely different images.
-        final float threshold = 0.1f;
         BitmapComparer inverseComparer = new BitmapComparer() {
             @Override
             public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
                     int height) {
+
+                // Return true if the images aren't even 10% similar. They should be completely
+                // different, since they should both be completely different colors.
+                final float threshold = 0.1f;
                 return !(new MSSIMComparer(threshold)).verifySame(ideal, given, offset, stride,
                         width, height);
             }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
index 4667ee9..ff1e9db 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
@@ -34,7 +34,7 @@
     @SmallTest
     public void testSimpleRectLayout() {
         createTest().addLayout(R.layout.simple_rect_layout, null, false).runWithVerifier(
-                new RectVerifier(Color.WHITE, Color.BLUE, new Rect(0, 0, 100, 100)));
+                new RectVerifier(Color.WHITE, Color.BLUE, new Rect(5, 5, 85, 85)));
     }
 }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 8df8057..6911cf0 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -17,7 +17,7 @@
 import com.android.cts.uirendering.R;
 
 public class PathClippingTests extends ActivityTestBase {
-    // draw circle with whole in it, with stroked circle
+    // draw circle with hole in it, with stroked circle
     static final CanvasClient sCircleDrawCanvasClient = new CanvasClient() {
         @Override
         public String getDebugString() {
@@ -31,11 +31,11 @@
             paint.setColor(Color.BLUE);
             paint.setStyle(Paint.Style.STROKE);
             paint.setStrokeWidth(20);
-            canvas.drawCircle(50, 50, 40, paint);
+            canvas.drawCircle(30, 30, 40, paint);
         }
     };
 
-    // draw circle with whole in it, by path operations + path clipping
+    // draw circle with hole in it, by path operations + path clipping
     static final CanvasClient sCircleClipCanvasClient = new CanvasClient() {
         @Override
         public String getDebugString() {
@@ -47,8 +47,8 @@
             canvas.save();
 
             Path path = new Path();
-            path.addCircle(50, 50, 50, Path.Direction.CW);
-            path.addCircle(50, 50, 30, Path.Direction.CCW);
+            path.addCircle(30, 30, 50, Path.Direction.CW);
+            path.addCircle(30, 30, 30, Path.Direction.CCW);
 
             canvas.clipPath(path);
             canvas.drawColor(Color.BLUE);
@@ -72,12 +72,12 @@
                 .runWithVerifier(new SamplePointVerifier(
                         new Point[] {
                                 // inside of circle
-                                new Point(50, 50),
+                                new Point(30, 50),
                                 // on circle
-                                new Point(50 + 32, 50 + 32),
+                                new Point(30 + 32, 30 + 32),
                                 // outside of circle
-                                new Point(50 + 38, 50 + 38),
-                                new Point(100, 100)
+                                new Point(30 + 38, 30 + 38),
+                                new Point(80, 80)
                         },
                         new int[] {
                                 Color.WHITE,
@@ -96,8 +96,8 @@
                         ViewGroup rootView = (ViewGroup) view;
                         rootView.setClipChildren(true);
                         View childView = rootView.getChildAt(0);
-                        childView.setPivotX(50);
-                        childView.setPivotY(50);
+                        childView.setPivotX(40);
+                        childView.setPivotY(40);
                         childView.setRotation(45f);
 
                     }
@@ -105,11 +105,11 @@
                 .runWithVerifier(new SamplePointVerifier(
                         new Point[] {
                                 // inside of rotated rect
-                                new Point(50, 50),
-                                new Point(50 + 32, 50 + 32),
+                                new Point(40, 40),
+                                new Point(40 + 25, 40 + 25),
                                 // outside of rotated rect
-                                new Point(50 + 38, 50 + 38),
-                                new Point(100, 100)
+                                new Point(40 + 31, 40 + 31),
+                                new Point(80, 80)
                         },
                         new int[] {
                                 Color.BLUE,
@@ -128,15 +128,15 @@
                         canvas.save();
 
                         Path path = new Path();
-                        path.addCircle(0, 50, 50, Path.Direction.CW);
-                        path.addCircle(100, 50, 50, Path.Direction.CW);
+                        path.addCircle(0, 45, 45, Path.Direction.CW);
+                        path.addCircle(90, 45, 45, Path.Direction.CW);
                         canvas.clipPath(path);
 
                         Paint paint = new Paint();
                         paint.setAntiAlias(true);
-                        paint.setTextSize(100);
+                        paint.setTextSize(90);
                         paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
-                        canvas.drawText("STRING", 0, 100, paint);
+                        canvas.drawText("STRING", 0, 90, paint);
 
                         canvas.restore();
                     }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
index 7947286..32ab0e4 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
@@ -22,6 +22,7 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Shader;
@@ -50,17 +51,11 @@
     public static final int MULTIPLY_COLOR = 0xFF668844;
     public static final int SCREEN_COLOR = 0xFFFFEEFF;
 
-    public static final int FILTER_COLOR = 0xFFBB0000;
-    public static final int RECT0_COLOR = 0x33808080;
-    public static final int RECT1_COLOR = 0x66808080;
-    public static final int RECT2_COLOR = 0x99808080;
-    public static final int RECT3_COLOR = 0xCC808080;
-
     // These points are in pairs, the first being the lower left corner, the second is only in the
     // Destination bitmap, the third is the intersection of the two bitmaps, and the fourth is in
     // the Source bitmap.
     private final static Point[] XFERMODE_TEST_POINTS = new Point[] {
-            new Point(1, 160), new Point(50, 50), new Point(70, 70), new Point(140, 140)
+            new Point(1, 80), new Point(25, 25), new Point(35, 35), new Point(70, 70)
     };
 
     /**
@@ -128,8 +123,8 @@
     };
 
     private final static DisplayModifier XFERMODE_MODIFIER = new DisplayModifier() {
-        private final RectF mSrcRect = new RectF(60, 60, 160, 160);
-        private final RectF mDstRect = new RectF(20, 20, 120, 120);
+        private final RectF mSrcRect = new RectF(30, 30, 80, 80);
+        private final RectF mDstRect = new RectF(10, 10, 60, 60);
         private final Bitmap mSrcBitmap = createSrc();
         private final Bitmap mDstBitmap = createDst();
 
@@ -144,8 +139,7 @@
         }
 
         private Bitmap createSrc() {
-            Bitmap srcB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap srcB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Canvas srcCanvas = new Canvas(srcB);
             Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
             srcPaint.setColor(SRC_COLOR);
@@ -154,8 +148,7 @@
         }
 
         private Bitmap createDst() {
-            Bitmap dstB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap dstB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Canvas dstCanvas = new Canvas(dstB);
             Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
             dstPaint.setColor(DST_COLOR);
@@ -164,18 +157,22 @@
         }
     };
 
-
     // We care about one point in each of the four rectangles of different alpha values, as well as
     // the area outside the rectangles
     private final static Point[] COLOR_FILTER_ALPHA_POINTS = new Point[] {
-            new Point(15, 90), new Point(45, 90), new Point(75, 90), new Point(105, 90),
-            new Point(135, 90)
+            new Point(9, 45),
+            new Point(27, 45),
+            new Point(45, 45),
+            new Point(63, 45),
+            new Point(81, 45)
     };
 
-    private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP = new LinkedHashMap<PorterDuff.Mode, int[]>() {
+    public static final int FILTER_COLOR = 0xFFBB0000;
+    private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP
+            = new LinkedHashMap<PorterDuff.Mode, int[]>() {
         {
             put(PorterDuff.Mode.SRC, new int[] {
-                FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
+                    FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
             });
 
             put(PorterDuff.Mode.DST, new int[] {
@@ -228,10 +225,17 @@
         }
     };
 
+    /**
+     * Draws 5 blocks of different color/opacity to be blended against
+     */
     private final static DisplayModifier COLOR_FILTER_ALPHA_MODIFIER = new DisplayModifier() {
-        private final static int mBlockWidths = 30;
-        private final int[] mColorValues = new int[] {RECT0_COLOR, RECT1_COLOR, RECT2_COLOR,
-                RECT3_COLOR};
+        private final int[] BLOCK_COLORS = new int[] {
+                0x33808080,
+                0x66808080,
+                0x99808080,
+                0xCC808080,
+                0x00000000
+        };
 
         private final Bitmap mBitmap = createQuadRectBitmap();
 
@@ -240,13 +244,15 @@
         }
 
         private Bitmap createQuadRectBitmap() {
-            Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Canvas canvas = new Canvas(bitmap);
             Paint paint = new Paint();
-            for (int i = 0 ; i < 4 ; i++) {
-                paint.setColor(mColorValues[i]);
-                canvas.drawRect(i * mBlockWidths, 0, (i + 1) * mBlockWidths, MODIFIER_HEIGHT, paint);
+            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+            final int blockCount = BLOCK_COLORS.length;
+            final int blockWidth = TEST_WIDTH / blockCount;
+            for (int i = 0 ; i < blockCount; i++) {
+                paint.setColor(BLOCK_COLORS[i]);
+                canvas.drawRect(i * blockWidth, 0, (i + 1) * blockWidth, TEST_HEIGHT, paint);
             }
             return bitmap;
         }
@@ -266,10 +272,9 @@
         }
 
         private Bitmap createGradient() {
-            LinearGradient gradient = new LinearGradient(30, 90, 150, 90, mColors, null,
+            LinearGradient gradient = new LinearGradient(15, 45, 75, 45, mColors, null,
                     Shader.TileMode.REPEAT);
-            Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
-                    Bitmap.Config.ARGB_8888);
+            Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
             Paint p = new Paint();
             p.setShader(gradient);
             Canvas c = new Canvas(bitmap);
@@ -281,9 +286,7 @@
     public static final DisplayModifier mCircleDrawModifier = new DisplayModifier() {
         @Override
         public void modifyDrawing(Paint paint, Canvas canvas) {
-            canvas.drawCircle(ActivityTestBase.TEST_WIDTH / 2,
-                    ActivityTestBase.TEST_HEIGHT / 2,
-                    ActivityTestBase.TEST_HEIGHT / 2, paint);
+            canvas.drawCircle(TEST_WIDTH / 2, TEST_HEIGHT / 2, TEST_HEIGHT / 2, paint);
         }
     };
 
@@ -343,16 +346,15 @@
     }
 
     /*
-     * TODO: fix this test for L MR1
     @SmallTest
     public void testShaderSweeps() {
-        int mask = DisplayModifier.Accessor.AA_MASK |
-                DisplayModifier.Accessor.SHADER_MASK |
-                DisplayModifier.Accessor.XFERMODE_MASK |
-                DisplayModifier.Accessor.SHAPES_MASK;
+        int mask = DisplayModifier.Accessor.AA_MASK
+                | DisplayModifier.Accessor.SHADER_MASK
+                | DisplayModifier.Accessor.XFERMODE_MASK
+                | DisplayModifier.Accessor.SHAPES_MASK;
         sweepModifiersForMask(mask, null, DEFAULT_MSSIM_COMPARER, null);
     }
-     */
+    */
 
     protected void sweepModifiersForMask(int mask, final DisplayModifier drawOp,
             BitmapComparer[] bitmapComparers, BitmapVerifier[] bitmapVerifiers) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
index da2db48..343228f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -20,11 +20,11 @@
  * Since the layout is blue on a white background, this is always done with a RectVerifier.
  */
 public class ViewClippingTests extends ActivityTestBase {
-    final Rect FULL_RECT = new Rect(0, 0, 200, 200);
-    final Rect BOUNDS_RECT = new Rect(0, 0, 100, 100);
-    final Rect PADDED_RECT = new Rect(15, 16, 83, 82);
-    final Rect OUTLINE_RECT = new Rect(1, 2, 98, 99);
-    final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 70, 80);
+    final Rect FULL_RECT = new Rect(0, 0, 90, 90);
+    final Rect BOUNDS_RECT = new Rect(0, 0, 80, 80);
+    final Rect PADDED_RECT = new Rect(15, 16, 63, 62);
+    final Rect OUTLINE_RECT = new Rect(1, 2, 78, 79);
+    final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 50, 60);
 
     final ViewInitializer BOUNDS_CLIP_INIT = new ViewInitializer() {
         @Override
@@ -61,10 +61,9 @@
         }
     };
 
-    // TODO: attempt to reduce
-    static final int TOLERANCE = 16;
     static BitmapVerifier makeClipVerifier(Rect blueBoundsRect) {
-        return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, TOLERANCE);
+        // very high error tolerance, since all these tests care about is clip alignment
+        return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, 75);
     }
 
     public void testSimpleUnclipped() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 386c015..e3ad5a6 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -41,15 +41,16 @@
     public static final String TAG = "ActivityTestBase";
     public static final boolean DEBUG = false;
     public static final boolean USE_RS = false;
-    public static final int TEST_WIDTH = 180;
-    public static final int TEST_HEIGHT = 180; //The minimum height and width of a device
+
+    //The minimum height and width of a device
+    public static final int TEST_WIDTH = 90;
+    public static final int TEST_HEIGHT = 90;
+
     public static final int MAX_SCREEN_SHOTS = 100;
 
     private int[] mHardwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
     private int[] mSoftwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
     private DifferenceVisualizer mDifferenceVisualizer;
-    private Allocation mIdealAllocation;
-    private Allocation mGivenAllocation;
     private RenderScript mRenderScript;
     private TestCaseBuilder mTestCaseBuilder;
 
@@ -122,16 +123,24 @@
         return pixels;
     }
 
+    private Bitmap takeScreenshotImpl() {
+        Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
+        int x = (source.getWidth() - TEST_WIDTH) / 2;
+        int y = (source.getHeight() - TEST_HEIGHT) / 2;
+        return Bitmap.createBitmap(source, x, y, TEST_WIDTH, TEST_HEIGHT);
+    }
+
     public Bitmap takeScreenshot() {
         getInstrumentation().waitForIdleSync();
-        Bitmap bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+        Bitmap bitmap1 = takeScreenshotImpl();
         Bitmap bitmap2;
         int count = 0;
         do  {
             bitmap2 = bitmap1;
-            bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+            bitmap1 = takeScreenshotImpl();
             count++;
-        } while (count < MAX_SCREEN_SHOTS && !Arrays.equals(getBitmapPixels(bitmap2), getBitmapPixels(bitmap1)));
+        } while (count < MAX_SCREEN_SHOTS &&
+                !Arrays.equals(getBitmapPixels(bitmap2), getBitmapPixels(bitmap1)));
         return bitmap1;
     }
 
@@ -161,12 +170,12 @@
         boolean success;
 
         if (USE_RS && comparer.supportsRenderScript()) {
-            mIdealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
+            Allocation idealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-            mGivenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
+            Allocation givenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-            success = comparer.verifySameRS(getActivity().getResources(), mIdealAllocation,
-                    mGivenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
+            success = comparer.verifySameRS(getActivity().getResources(), idealAllocation,
+                    givenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
         } else {
             bitmap1.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
             bitmap2.getPixels(mHardwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
index 92242f0..60127ae 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
@@ -19,6 +19,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.util.AttributeSet;
 import android.view.View;
 
 /**
@@ -26,14 +27,21 @@
  */
 public class CanvasClientView extends View {
     private CanvasClient mCanvasClient;
-    private int mWidth;
-    private int mHeight;
 
-    public CanvasClientView(Context context, CanvasClient canvasClient, int width, int height) {
+    public CanvasClientView(Context context) {
         super(context);
+    }
+
+    public CanvasClientView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CanvasClientView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setCanvasClient(CanvasClient canvasClient) {
         mCanvasClient = canvasClient;
-        mWidth = width;
-        mHeight = height;
     }
 
     @Override
@@ -45,11 +53,11 @@
             paint.setTextSize(20);
             canvas.drawText(s, 200, 200, paint);
         }
-        if (mCanvasClient != null) {
-            canvas.save();
-            canvas.clipRect(0, 0, mWidth, mHeight);
-            mCanvasClient.draw(canvas, mWidth, mHeight);
-            canvas.restore();
-        }
+        if (mCanvasClient == null) throw new IllegalStateException("Canvas client missing");
+
+        canvas.save();
+        canvas.clipRect(0, 0, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        mCanvasClient.draw(canvas, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        canvas.restore();
     }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
index 684293d..b42ac88 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -32,17 +32,16 @@
  * Modifies the canvas and paint objects when called.
  */
 public abstract class DisplayModifier {
-    private static final RectF gRect = new RectF(0, 0, 100, 100);
-    private static final float[] gPts = new float[]{
-            0, 100, 100, 0, 100, 200, 200, 100
+    private static final RectF RECT = new RectF(0, 0, 100, 100);
+    private static final float[] POINTS = new float[]{
+            0, 40, 40, 0, 40, 80, 80, 40
     };
-    private static final float[] gTriPts = new float[]{
-            75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
+    private static final float[] TRIANGLE_POINTS = new float[]{
+            40, 0, 80, 80, 80, 80, 0, 80, 0, 80, 40, 0
     };
-    private static final int NUM_PARALLEL_LINES = 24;
-    private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
-    protected static final int MODIFIER_WIDTH = 180;
-    protected static final int MODIFIER_HEIGHT = 180;
+    private static final int NUM_PARALLEL_LINES = 10;
+    private static final float[] LINES = new float[NUM_PARALLEL_LINES * 8
+            + TRIANGLE_POINTS.length];
 
     public static final PorterDuff.Mode[] PORTERDUFF_MODES = new PorterDuff.Mode[] {
         PorterDuff.Mode.SRC, PorterDuff.Mode.DST, PorterDuff.Mode.SRC_OVER,
@@ -53,25 +52,23 @@
     };
 
     static {
-        int index;
-        for (index = 0; index < gTriPts.length; index++) {
-            gLinePts[index] = gTriPts[index];
-        }
+        System.arraycopy(TRIANGLE_POINTS, 0, LINES, 0, TRIANGLE_POINTS.length);
+        int index = TRIANGLE_POINTS.length;
         float val = 0;
         for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
-            gLinePts[index + 0] = 150;
-            gLinePts[index + 1] = val;
-            gLinePts[index + 2] = 300;
-            gLinePts[index + 3] = val;
+            LINES[index + 0] = 40;
+            LINES[index + 1] = val;
+            LINES[index + 2] = 80;
+            LINES[index + 3] = val;
             index += 4;
             val += 8 + (2.0f / NUM_PARALLEL_LINES);
         }
         val = 0;
         for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
-            gLinePts[index + 0] = val;
-            gLinePts[index + 1] = 150;
-            gLinePts[index + 2] = val;
-            gLinePts[index + 3] = 300;
+            LINES[index + 0] = val;
+            LINES[index + 1] = 40;
+            LINES[index + 2] = val;
+            LINES[index + 3] = 80;
             index += 4;
             val += 8 + (2.0f / NUM_PARALLEL_LINES);
         }
@@ -81,7 +78,7 @@
     // paint object, like anti-aliasing or drawing. Within those LinkedHashMaps are the various
     // options for that specific topic, which contains a displaymodifier which will affect the
     // given canvas and paint objects.
-    public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> sMaps =
+    public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> MAPS =
             new LinkedHashMap<String, LinkedHashMap<String,DisplayModifier>>() {
                 {
                     put("aa", new LinkedHashMap<String, DisplayModifier>() {
@@ -225,7 +222,7 @@
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
                                     canvas.rotate(90);
-                                    canvas.translate(0, -200);
+                                    canvas.translate(0, -100);
                                 }
                             });
                             put("scale2x2", new DisplayModifier() {
@@ -347,13 +344,13 @@
                             put("roundRect", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawRoundRect(gRect, 20, 20, paint);
+                                    canvas.drawRoundRect(RECT, 20, 20, paint);
                                 }
                             });
                             put("rect", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawRect(gRect, paint);
+                                    canvas.drawRect(RECT, paint);
                                 }
                             });
                             put("circle", new DisplayModifier() {
@@ -365,36 +362,32 @@
                             put("oval", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawOval(gRect, paint);
+                                    canvas.drawOval(RECT, paint);
                                 }
                             });
                             put("lines", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawLines(gLinePts, paint);
+                                    canvas.drawLines(LINES, paint);
                                 }
                             });
-                            /* drawPoints does not work with zero stroke width,
-                             * but it isn't a regression
-                             * TODO: fix hardware canvas so that drawPoints works
                             put("plusPoints", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawPoints(gPts, paint);
+                                    canvas.drawPoints(POINTS, paint);
                                 }
                             });
-                             */
                             put("text", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    paint.setTextSize(36);
+                                    paint.setTextSize(20);
                                     canvas.drawText("TEXTTEST", 0, 50, paint);
                                 }
                             });
                             put("shadowtext", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    paint.setTextSize(36);
+                                    paint.setTextSize(20);
                                     paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
                                     canvas.drawText("TEXTTEST", 0, 50, paint);
                                 }
@@ -410,13 +403,13 @@
                             put("arc", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawArc(gRect, 260, 285, false, paint);
+                                    canvas.drawArc(RECT, 260, 285, false, paint);
                                 }
                             });
                             put("arcFromCenter", new DisplayModifier() {
                                 @Override
                                 public void modifyDrawing(Paint paint, Canvas canvas) {
-                                    canvas.drawArc(gRect, 260, 285, true, paint);
+                                    canvas.drawArc(RECT, 260, 285, true, paint);
                                 }
                             });
                         }
@@ -454,9 +447,9 @@
             // Create a Display Map of the valid indices
             mDisplayMap = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>();
             int index = 0;
-            for (String key : DisplayModifier.sMaps.keySet()) {
+            for (String key : DisplayModifier.MAPS.keySet()) {
                 if (validIndex(index)) {
-                    mDisplayMap.put(key, DisplayModifier.sMaps.get(key));
+                    mDisplayMap.put(key, DisplayModifier.MAPS.get(key));
                 }
                 index++;
             }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 166b6ff..65ed7ee 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.view.View;
+import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.webkit.WebView;
 
@@ -41,6 +42,8 @@
 
     public void onCreate(Bundle bundle){
         super.onCreate(bundle);
+        getWindow().getDecorView().setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
         mHandler = new RenderSpecHandler();
         int uiMode = getResources().getConfiguration().uiMode;
         mOnWatch = (uiMode & Configuration.UI_MODE_TYPE_WATCH) == Configuration.UI_MODE_TYPE_WATCH;
@@ -83,39 +86,40 @@
         }
 
         public void handleMessage(Message message) {
-            int webViewBuffer = 0;
+            int drawCountDelay = 0;
+            setContentView(R.layout.test_container);
+            ViewStub stub = (ViewStub) findViewById(R.id.test_content_stub);
             switch (message.what) {
                 case LAYOUT_MSG: {
-                    setContentView(message.arg1);
-                    mView = findViewById(R.id.test_root);
-                    if (mView == null) {
-                        throw new IllegalStateException("test_root failed to inflate");
-                    }
+                    stub.setLayoutResource(message.arg1);
+                    mView = stub.inflate();
                 } break;
 
                 case CANVAS_MSG: {
-                    mView = new CanvasClientView(getApplicationContext(),
-                            (CanvasClient) (message.obj), ActivityTestBase.TEST_WIDTH,
-                            ActivityTestBase.TEST_HEIGHT);
-                    setContentView(mView);
+                    stub.setLayoutResource(R.layout.test_content_canvasclientview);
+                    mView = stub.inflate();
+                    ((CanvasClientView) mView).setCanvasClient((CanvasClient) (message.obj));
                 } break;
 
                 case WEB_VIEW_MSG: {
-                    mView = new WebView(getApplicationContext());
+                    stub.setLayoutResource(R.layout.test_content_webview);
+                    mView = stub.inflate();
                     ((WebView) mView).loadUrl((String) message.obj);
                     ((WebView) mView).setInitialScale(100);
-                    setContentView(mView);
-                    webViewBuffer = 10;
+                    drawCountDelay = 10;
                 } break;
             }
 
+            if (mView == null) {
+                throw new IllegalStateException("failed to inflate test content");
+            }
+
             if (mViewInitializer != null) {
                 mViewInitializer.intializeView(mView);
             }
-
             mView.setLayerType(message.arg2, null);
 
-            DrawCounterListener onDrawListener = new DrawCounterListener(webViewBuffer);
+            DrawCounterListener onDrawListener = new DrawCounterListener(drawCountDelay);
 
             mView.getViewTreeObserver().addOnPreDrawListener(onDrawListener);
 
diff --git a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
index 40d1d3d..6007730 100644
--- a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
@@ -19,7 +19,6 @@
 import com.android.cts.view.R;
 import com.android.internal.view.menu.MenuBuilder;
 
-
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
@@ -28,6 +27,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.SubMenu;
@@ -48,17 +48,22 @@
     protected void setUp() throws Exception {
         super.setUp();
         mActivity = getActivity();
-        mMenuInflater = mActivity.getMenuInflater();
     }
 
+    @UiThreadTest
     public void testConstructor() {
         new MenuInflater(mActivity);
     }
 
+    @UiThreadTest
     public void testInflate() {
         Menu menu = new MenuBuilder(mActivity);
         assertEquals(0, menu.size());
 
+        if (mMenuInflater == null) {
+            mMenuInflater = mActivity.getMenuInflater();
+        }
+
         mMenuInflater.inflate(com.android.cts.view.R.menu.browser, menu);
         assertNotNull(menu);
         assertEquals(1, menu.size());
@@ -77,7 +82,12 @@
     }
 
     // Check wheher the objects are created correctly from xml files
+    @UiThreadTest
     public void testInflateFromXml(){
+        if (mMenuInflater == null) {
+            mMenuInflater = mActivity.getMenuInflater();
+        }
+
         // the visibility and shortcut
         Menu menu = new MenuBuilder(mActivity);
         mMenuInflater.inflate(R.menu.visible_shortcut, menu);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 24b8fdb..480e1a6 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -2845,15 +2845,19 @@
         assertEquals(1, mTextView.getImeActionId());
     }
 
-    @UiThreadTest
     public void testSetTextLong() {
-        final int MAX_COUNT = 1 << 21;
-        char[] longText = new char[MAX_COUNT];
-        for (int n = 0; n < MAX_COUNT; n++) {
-            longText[n] = 'm';
-        }
-        mTextView = findTextView(R.id.textview_text);
-        mTextView.setText(new String(longText));
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                final int MAX_COUNT = 1 << 21;
+                char[] longText = new char[MAX_COUNT];
+                for (int n = 0; n < MAX_COUNT; n++) {
+                    longText[n] = 'm';
+                }
+                mTextView = findTextView(R.id.textview_text);
+                mTextView.setText(new String(longText));
+            }
+        });
+        mInstrumentation.waitForIdleSync();
     }
 
     @UiThreadTest
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index c43183a..8544a93 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -31,7 +31,7 @@
     @Option(name="cts-install-path", description="the path to the cts installation to use")
     private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
 
-    public static final String CTS_BUILD_VERSION = "5.0_r2.5";
+    public static final String CTS_BUILD_VERSION = "5.1_r0.5";
 
     /**
      * {@inheritDoc}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
index 3d92eb3..72dccd4 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
@@ -185,6 +185,11 @@
     }
 
     @Override
+    public void setTestCollection(boolean b) {
+        throw new UnsupportedOperationException("Test Collection mode is not supported");
+    }
+
+    @Override
     public void setTestSize(TestSize size) {
         addInstrumentationArg(SIZE_ARG_NAME, ""/*size.getRunnerValue()*/);
     }