Merge "camera2: Update RobustnessTest timeouts." into lmp-mr1-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index ac1ad03..e67a5c1 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1275,8 +1275,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>
@@ -1289,12 +1288,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>
@@ -1399,6 +1399,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/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 f600aa3..0724e0f 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1104,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">
@@ -1426,4 +1440,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/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index b697be8..12aa37b 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,8 @@
     private TestItem mDeviceAdminVisibleTest;
     private TestItem mWorkAppVisibleTest;
     private TestItem mCrossProfileIntentFiltersTest;
+    private TestItem mDisableNonMarketTest;
+    private TestItem mEnableNonMarketTest;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -179,6 +181,16 @@
             }
         };
 
+        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));
         mCrossProfileIntentFiltersTest = new TestItem(this,
@@ -191,6 +203,8 @@
         mTests.add(mDeviceAdminVisibleTest);
         mTests.add(mWorkAppVisibleTest);
         mTests.add(mCrossProfileIntentFiltersTest);
+        mTests.add(mDisableNonMarketTest);
+        mTests.add(mEnableNonMarketTest);
     }
 
     @Override
@@ -389,5 +403,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..1f78daf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -50,6 +50,7 @@
             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);
             dpm.addCrossProfileIntentFilter(getWho(context), filter,
                     DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
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/tests/tests/app/src/android/app/cts/AlarmManagerTest.java b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
index bfc3e1d..b9caae9 100644
--- a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -47,6 +47,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 +222,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 slightly beyond the initial alarm to verify that it fires the first time
+        new PollingCheck(TEST_ALARM_FUTURITY + TIME_DELTA) {
             @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 + TIME_DELTA) {
             @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 {
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/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/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/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()*/);
     }