Added scripts and update java files for Verifier type power test.

Change-Id: If8d272a42aa264b12e7f723157f1922aeed6a94b

Conflicts:
	apps/CtsVerifier/AndroidManifest.xml
	apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java
	apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 021f724..990287e 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -59,6 +59,19 @@
 verifier-zip-name := $(verifier-dir-name).zip
 verifier-zip := $(cts-dir)/$(verifier-zip-name)
 
+$(PRODUCT_OUT)/data/app/CtsVerifier.apk $(verifier-zip): $(verifier-dir)/power/execute_power_tests.py
+$(PRODUCT_OUT)/data/app/CtsVerifier.apk $(verifier-zip): $(verifier-dir)/power/power_monitors/monsoon.py
+
+#Copy the necessary host-side scripts to include in the
+#zip file:
+$(verifier-dir)/power/power_monitors/monsoon.py: cts/apps/CtsVerifier/assets/scripts/power_monitors/monsoon.py
+	$(hide) mkdir -p $(verifier-dir)/power/power_monitors
+	$(hide)  $(ACP) -fp  cts/apps/CtsVerifier/assets/scripts/power_monitors/*.py $(verifier-dir)/power/power_monitors/.
+
+$(verifier-dir)/power/execute_power_tests.py: cts/apps/CtsVerifier/assets/scripts/execute_power_tests.py
+	$(hide) mkdir -p $(verifier-dir)/power
+	$(hide)  $(ACP) -fp   cts/apps/CtsVerifier/assets/scripts/execute_power_tests.py $@
+
 cts : $(verifier-zip)
 ifeq ($(HOST_OS),linux)
 $(verifier-zip) : $(HOST_OUT)/bin/cts-usb-accessory
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1f95501..4e57588 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -878,6 +878,16 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.usb.accessory" />
         </activity>
 
+        <activity android:name=".sensors.SensorPowerTestActivity"
+                android:label="@string/sensor_power_test"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <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_sensors" />
+        </activity>
+
         <activity android:name=".p2p.P2pTestListActivity"
                 android:label="@string/p2p_test"
                 android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/apps/CtsVerifier/assets/scripts/execute_power_tests.py b/apps/CtsVerifier/assets/scripts/execute_power_tests.py
new file mode 100755
index 0000000..71d2850
--- /dev/null
+++ b/apps/CtsVerifier/assets/scripts/execute_power_tests.py
@@ -0,0 +1,563 @@
+#!/usr/bin/python
+
+# 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.
+
+import logging
+import os.path
+import select
+import sys
+import time
+import collections
+import socket
+import gflags as flags  # http://code.google.com/p/python-gflags/
+import pkgutil
+import threading
+
+# let this script know about the power monitor impementations
+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('_')]
+
+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 to do so
+USE_STRICT_DELAY = False
+if USE_STRICT_DELAY:
+    DELAY_SCREEN_OFF = 30.0
+else:
+    DELAY_SCREEN_OFF = 2.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('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+class PowerTest:
+    """Class to run a suite of power tests"""
+
+    #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
+    
+    
+    
+    PORT = 0  # any available port 
+    DOMAIN_NAME = "/android/cts/powertest"
+    SAMPLE_COUNT_NOMINAL = 1000
+    RATE_NOMINAL = 100
+
+    QUERY_EXTERNAL_STORAGE = "EXTERNAL STORAGE?"
+    
+    def __init__(self):
+        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)
+        print ("Establishing connection to device...")
+        self.setUSBEnabled(True)
+        status = self._power_monitor.GetStatus()
+        self._native_hz = status["sampleRate"] * 1000
+        self._current_test = "None"
+        self._external_storage = self.executeOnDevice(PowerTest.QUERY_EXTERNAL_STORAGE, reportErrors=True )
+        
+    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("EXIT", reportErrors=False)
+            self.executeLocal("adb forward --remove tcp:%d" % PowerTest.PORT)
+        PowerTest.PORT = 0
+        if self._power_monitor:
+            self._power_monitor.Close()
+            self._power_monitor = None
+        
+    def _send(self, msg, report_errors=True):
+        """Connect to the device, send the given commmand, and then disconnect"""
+        if PowerTest.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()
+            dummysocket.close()
+            assert(PowerTest.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)
+            
+        link = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+        try:
+            logging.debug("Connecting to device...")
+            link.connect (("localhost", PowerTest.PORT))
+            logging.debug("Connected.")
+        except: 
+            if report_errors:
+                self.reportErrorIf(True, msg="Unable to communicate with device: connection refused")
+        logging.debug("Sending '%s'" % msg)
+        link.sendall(msg)
+        logging.debug("Getting response...")
+        response = link.recv(4096)
+        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)
+        return self._send(query) == "OK"
+        
+    def executeOnDevice(self, cmd , reportErrors=True):
+        """Execute a (string) command on the remote device"""
+        return self._send(cmd , reportErrors)
+    
+    def executeLocal(self, cmd, check_status=True):
+        """execute a shell command locally (on the host)"""
+        from subprocess import call
+        status = call(cmd.split(' '))
+        if status != 0 and check_status:
+            logging.error("Failed to execute \"%s\"" % cmd)
+        else:
+            logging.debug("Executed \"%s\"" % cmd)
+        return status
+        
+    def reportErrorIf(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"""
+        if condition:
+            try:
+                logging.error("Exiting on error: %s" % msg)
+                self.executeOnDevice("RAISE " + self._current_test + " " + msg , False)
+            except:
+                
+                logging.error("Unable to communicate with device to report error: %s" % msg)
+                self.finalize()
+                sys.exit(msg)
+            raise Exception(msg)
+                
+    def setUSBEnabled(self, enabled, verbose=True):
+        if enabled:
+            val = 1
+        else:
+            val = 0
+        self._power_monitor.SetUsbPassthrough(val)
+        tries = 0
+        
+        #Sometimes command won't go through first time, paricularly if
+        #immediately after a data collection, so allow for retries
+        status = self._power_monitor.GetStatus()
+        while status is None and tries < 5:
+            tries += 1
+            time.sleep(2.0)
+            logging.error("Retrying get status call...")
+            self._power_monitor.StopDataCollection()
+            self._power_monitor.SetUsbPassthrough(val)
+            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")
+        else:
+            if verbose: logging.info("...USB disabled")
+        # re-establish port forwarding
+        if enabled and PowerTest.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")
+                 
+    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
+        
+        # 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  # mAmps
+        TIMEOUT_SCREEN_OFF = 30 #this many tries at most
+        count_good = 0
+        tries = 0
+        print("Waiting for screen off...")
+        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 tries >= TIMEOUT_SCREEN_OFF:
+                self.reportErrorIf(tries>=TIMEOUT_SCREEN_OFF, msg="Unable to determine screen off status.")
+                break
+        if DELAY_SCREEN_OFF:
+            #add additional delay time if necessary
+            time.sleep(DELAY_SCREEN_OFF)
+        print("...Screen off.")
+        
+    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...")
+        self._power_monitor.StartDataCollection()
+        sub_measurements = []
+        measurements = []
+        tries = 0
+        if verbose: print("")
+        try:
+            while len(measurements) < measurementCount and tries < 5:
+                if tries:
+                    #logging.error("Failed attempt %d" % tries)
+                    self._power_monitor.StopDataCollection()
+                    self._power_monitor.StartDataCollection()
+                    time.sleep(1.0)
+                tries += 1            
+                additional = self._power_monitor.CollectData()
+                if additional is not None:
+                    tries = 0
+                    sub_measurements.extend(additional)
+                    while len(sub_measurements) >= decimate_by:
+                        sub_avg = sum(sub_measurements) / len(sub_measurements)
+                        measurements.append(sub_avg) 
+                        sub_measurements = sub_measurements[decimate_by:]
+                        if verbose:
+                            sys.stdout.write("\33[1A\33[2K")
+                            print ("MEASURED[%d]: %f" % (len(measurements),measurements[-1]))
+        finally:
+            self._power_monitor.StopDataCollection()
+        
+        self.reportErrorIf(measurementCount > len(measurements),
+                            "Unable to collect all requested measurements")
+        return measurements
+    
+    def request_user_acknowledgment(self, msg):
+        """Post message to user on screen and wait for acknowledgment"""
+        response = self.executeOnDevice("REQUEST USER RESPONSE " + msg)
+        self.reportErrorIf(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("SET TEST RESULT %s %s %s" % (testname, val, msg))
+        self.reportErrorIf(response != "OK", "Unable to send test status to Verifier")
+
+    def setPowerOn(self, sensor, powered_on):
+        response = self.executeOnDevice("SENSOR %s %s" % ({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]))
+        return response
+    
+    def runPowerTest(self, sensor, max_power_allowed, user_request = None):
+        self._current_test = "%s_Power_Test_While_%s" % (sensor, {True:"Under_Motion", False:"Still"}[user_request is not None])
+        try:
+            print ("\n\n---------------------------------")
+            if user_request is not None:
+                print ("Running power test on %s under motion." % sensor)
+            else:
+                print ("Running power test on %s while device is still." % sensor)
+            print ("---------------------------------")
+            response = self.executeOnDevice("SENSOR? %s"%sensor)
+            if response == "UNAVAILABLE":
+                self.setTestResult(self._current_test, condition="SKIPPED", msg="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)
+                return
+
+            self.reportErrorIf(response != "OK", "Unable to set all sensor 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.setUSBEnabled(False)
+            self.reportErrorIf(response != "OK", "Unable to set sensor %s ON" % sensor)
+            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:
+                    for m in measurements:
+                        f.write( "%.4f\n"%m)
+                    self.setUSBEnabled(True, verbose = False)
+                    print("Saving raw data files to device...")
+                    self.executeLocal("adb shell mkdir -p %s/sensor_power_test_data"%self._external_storage, False)
+                    self.executeLocal("adb push %s %s/sensor_power_test_data/."%(f.name, self._external_storage))
+                    self.setUSBEnabled(False, verbose = False)
+            self.reportErrorIf(not measurements, "No measurements could be taken for %s" % sensor)
+            avg = sum(measurements) / len(measurements)
+            squared = [(m-avg)*(m-avg) for m in measurements]
+
+            import math
+            stddev = math.sqrt(sum(squared)/len(squared))
+            current_diff = avg - backgnd
+            self.setUSBEnabled(True)
+            max_power = max(measurements) - avg
+            if current_diff <= max_power_allowed:
+                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)
+            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)
+        except:
+            import traceback
+            traceback.print_exc()
+            self.setTestResult(self._current_test, condition="FAIL", msg="Exception occurred during run of test.")
+        
+    
+    @staticmethod
+    def run_tests():
+        
+        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.\n" + \
+                 "Please 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.\n" + \
+                 "Please refrain from interacting with the screen or pressing any side buttons " + \
+                 "while measurements are taken."
+            testrunner = PowerTest()
+            testrunner.reportErrorIf(not testrunner.queryDevice("AIRPLANE MODE ON?"),
+                                      "Airplane mode not off as expected")
+            #testrunner.reportErrorIf(int(testrunner.executeOnDevice("SCREEN OFF TIMEOUT?")) > 15000,
+            #                          "Screen sleep time not set to 15 seconds")
+            testrunner.executeOnDevice("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)
+        except:            
+            import traceback
+            traceback.print_exc()
+        finally:
+            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,"+\
+                             " the CtsVerifier app is running the SensorPowerTest on the device, and USB passthrough 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
+  
+  if FLAGS.avg and FLAGS.avg < 0:
+    loggign.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 not FLAGS.power_monitor:
+      sys.exit("You must specify a '--power_monitor' option to specify which power monitor type you are using.\n"+
+               "One 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 passthrough 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()
+      
+if __name__ == "__main__":
+    flags.DEFINE_boolean("status", None, "Print power meter status")
+    flags.DEFINE_integer("avg", None,
+                         "Also report average over last n data points")
+    flags.DEFINE_float("voltage", None, "Set output voltage (0 for off)")
+    flags.DEFINE_float("current", None, "Set max output current")
+    flags.DEFINE_string("usbpassthrough", None, "USB control (on, off, auto)")
+    flags.DEFINE_integer("samples", None, "Collect and print this many samples")
+    flags.DEFINE_integer("hz", 5000, "Print this many samples/sec")
+    flags.DEFINE_string("device", None,
+                        "Path to the device in /dev/... (ex:/dev/ttyACM1)")
+    flags.DEFINE_boolean("timestamp", None,
+                         "Also print integer (seconds) timestamp on each line")
+    flags.DEFINE_boolean("ramp", True, "Gradually increase voltage")
+    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")
+    sys.exit(main(FLAGS(sys.argv)))
+    
+
diff --git a/apps/CtsVerifier/assets/scripts/power_monitors/__init__.py b/apps/CtsVerifier/assets/scripts/power_monitors/__init__.py
new file mode 100644
index 0000000..cec4545
--- /dev/null
+++ b/apps/CtsVerifier/assets/scripts/power_monitors/__init__.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+
+# 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.
+#
+
+from abc import abstractmethod
+
+class Abstract_Power_Monitor(object):
+  """
+  Provides a simple interface to use the power meter.
+  The implementer should establish communications with the power monitor
+  on __init__:
+
+  monitor = <<Implementing Class>>(...)
+
+  Afterward, data can be collected multiple times via the
+  series of calls, for example:
+
+      montior.StartDataCollection()
+      data = True
+      while data is not None:
+          data = monitor.CollectData()
+          <<do-something-with-data>>
+      monitor.StopDataCollection()
+
+  On exit, Close should be called:
+
+      monitor.Close()
+
+  The method GetStatus() can be used to check voltage setting as well as
+  ensure status of the USB passthrough connection.
+  """
+
+  
+  
+  def __init__(self ):
+      pass  
+
+  @abstractmethod
+  def Close(self):
+      """Close the connection to the device, preventing
+      further interactions.  This should be called only
+      when the power monitor is no longer needed
+      (e.g. on exit)
+      """
+      pass
+
+  @abstractmethod
+  def GetStatus(self):
+    """ Requests and waits for status.  Returns status dictionary.
+    This should at a minimum contain entries for "usbPassthroughMode"
+    and "outputVoltageSetting" """
+    pass
+
+  @abstractmethod
+  def SetVoltage(self, v):
+    """ Set the output voltage, 0 to disable. """
+    pass
+
+  @abstractmethod
+  def SetMaxCurrent(self, i):
+    """Set the max output current."""
+    pass
+    
+  @abstractmethod
+  def SetUsbPassthrough(self, val):
+    """ Set the USB passthrough mode: 0 = off, 1 = on,  2 = auto. """
+    pass
+
+  @abstractmethod
+  def StartDataCollection(self):    
+    """ Tell the device to start collecting and sending measurement data. """
+    pass
+    
+  @abstractmethod
+  def StopDataCollection(self):
+    """ Tell the device to stop collecting measurement data. """
+    pass
+    
+  @abstractmethod
+  def CollectData(self, verbose = True):
+    """ Return some current samples.  Call StartDataCollection() first.
+    Returns None if no data available"""
+    pass
diff --git a/apps/CtsVerifier/assets/scripts/power_monitors/_dummy.py b/apps/CtsVerifier/assets/scripts/power_monitors/_dummy.py
new file mode 100644
index 0000000..16c2f3c
--- /dev/null
+++ b/apps/CtsVerifier/assets/scripts/power_monitors/_dummy.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+# 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.
+
+from . import Abstract_Power_Monitor
+
+class Power_Monitor(Abstract_Power_Monitor):
+  """
+  Dummy implementation for internal use only to test host-to-device
+  interactions without need for a real power monitor.  This is not
+  to be used in any way as part of CtsVerifier or CTS verification
+  activities in general.
+  """
+
+  
+  def __init__(self, device = None, wait = False, log_file_id = None ):
+      self._device = device
+      self._usbpassthroughmode = 1
+      self._voltage = 0.0
+      self._data_active = False
+      self._sequence = 0
+      
+  def __del__(self):
+      self.Close()
+
+  def Close(self):
+      pass
+    
+  @staticmethod
+  def Discover():
+      return ["dummy_monitor"]
+
+  def GetStatus(self):
+    """ Requests and waits for status.  Returns status dictionary. """
+    return {"usbPassthroughMode": self._usbpassthroughmode,
+            "sampleRate":1}
+
+
+  def RampVoltage(self, start, end):
+      self._voltage = end   
+
+  def SetVoltage(self, v):
+    """ Set the output voltage, 0 to disable. """
+    self._voltage = v
+
+  def SetMaxCurrent(self, i):
+    """Set the max output current."""
+    self._max_current = i
+    
+  def SetUsbPassthrough(self, val):
+    """ Set the USB passthrough mode: 0 = off, 1 = on,  2 = auto. """
+    self._usbpassthroughmode = val
+
+  def StartDataCollection(self):    
+    """ Tell the device to start collecting and sending measurement data. """
+    self._data_active = True
+    
+  def StopDataCollection(self):
+    """ Tell the device to stop collecting measurement data. """
+    self._data_active = False
+    
+  def CollectData(self, verbose = True):
+    """ Return some current samples.  Call StartDataCollection() first. """
+    #self.log("Collecting data ...", debug = True)
+    import random
+    if self._data_active:
+        base = [0.003, 0.003, 0.003, (self._sequence%4)*0.0005]
+        self._sequence += 1
+        values = [ random.gauss(base[(self._sequence-1)%4], 0.0005) for _ in range(100)]
+    else:
+        values = None
+    return values
diff --git a/apps/CtsVerifier/assets/scripts/power_monitors/monsoon.py b/apps/CtsVerifier/assets/scripts/power_monitors/monsoon.py
new file mode 100644
index 0000000..c02f113
--- /dev/null
+++ b/apps/CtsVerifier/assets/scripts/power_monitors/monsoon.py
@@ -0,0 +1,433 @@
+#!/usr/bin/python
+
+# 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.
+
+
+import fcntl
+import logging
+logging.getLogger().setLevel(logging.ERROR)
+
+import os.path
+import select
+import stat
+import struct
+import sys
+import time
+import collections
+import socket
+import glob
+import signal
+import serial           # http://pyserial.sourceforge.net/
+
+#Set to True if you want log output to go to screen:
+LOG_TO_SCREEN = False
+
+TIMEOUT_SERIAL = 1 #seconds
+
+#ignore SIG CONTINUE signals
+for signum in [signal.SIGCONT]:              
+  signal.signal(signum, signal.SIG_IGN)
+
+try:
+  from . import Abstract_Power_Monitor
+except:
+  sys.exit("You cannot run 'monsoon.py' directly.  Run 'execut_power_tests.py' instead.")
+  
+class Power_Monitor(Abstract_Power_Monitor):
+  """
+  Provides a simple class to use the power meter, e.g.
+  mon = monsoon.Power_Monitor()
+  mon.SetVoltage(3.7)
+  mon.StartDataCollection()
+  mydata = []
+  while len(mydata) < 1000:
+    mydata.extend(mon.CollectData())
+  mon.StopDataCollection()
+  """
+  _do_log = False
+
+  @staticmethod
+  def lock( device ):
+      tmpname = "/tmp/monsoon.%s.%s" % ( os.uname()[0],
+                                         os.path.basename(device))
+      lockfile = open(tmpname, "w")
+      try:  # use a lockfile to ensure exclusive access
+          fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+          logging.debug("Locked device %s"%device)
+      except IOError as e:
+          self.log("device %s is in use" % dev)
+          sys.exit('device in use')
+      return lockfile
+  
+  def to_string(self):
+      return self._devicename
+  
+  def __init__(self, device = None, wait = False, log_file_id= None ):
+    """
+    Establish a connection to a Power_Monitor.
+    By default, opens the first available port, waiting if none are ready.
+    A particular port can be specified with "device".
+    With wait=0, IOError is thrown if a device is not immediately available.
+    """
+    self._lockfile = None
+    self._logfile = None
+    self.ser = None
+    for signum in [signal.SIGALRM, signal.SIGHUP, signal.SIGINT,
+                   signal.SIGILL, signal.SIGQUIT,
+                   signal.SIGTRAP,signal.SIGABRT, signal.SIGIOT, signal.SIGBUS,
+                   signal.SIGFPE, signal.SIGSEGV, signal.SIGUSR2, signal.SIGPIPE,
+                   signal.SIGTERM]:
+      signal.signal(signum, self.handle_signal)
+
+    self._coarse_ref = self._fine_ref = self._coarse_zero = self._fine_zero = 0
+    self._coarse_scale = self._fine_scale = 0
+    self._last_seq = 0
+    self.start_voltage = 0
+        
+    if device:
+      if isinstance( device, serial.Serial ):
+        self.ser = device
+        
+    else:
+        device_list = None
+        while not device_list:
+            device_list = Power_Monitor.Discover()
+            if not device_list and wait:
+                time.sleep(1.0)
+                logging.info("No power monitor serial devices found.  Retrying...")
+            elif not device_list and not wait:
+                logging.error("No power monitor serial devices found.  Exiting")
+                self.Close()
+                sys.exit("No power monitor serial devices found")
+                
+        if device_list:
+            if len(device_list) > 1:
+                logging.error("=======================================")
+                logging.error("More than one power monitor discovered!")
+                logging.error("Test may not execute properly.Aborting test.")
+                logging.error("=======================================")
+                sys.exit("More than one power monitor connected.")
+            device = device_list[0].to_string() # choose the first one
+            if len(device_list) > 1:
+                logging.info("More than one device found.  Using %s"%device)
+            else:
+                logging.info("Power monitor @ %s"%device)
+        else: raise IOError("No device found")
+          
+    self._lockfile = Power_Monitor.lock( device )
+    if log_file_id is not None:
+        self._logfilename = "/tmp/monsoon_%s_%s.%s.log" % (os.uname()[0], os.path.basename(device),
+                                                            log_file_id)
+        self._logfile = open(self._logfilename,'a')
+    else:
+        self._logfile = None
+    try:
+        self.ser = serial.Serial(device, timeout= TIMEOUT_SERIAL)
+    except Exception as e:
+      self.log( "error opening device %s: %s" % (dev, e))
+      self._lockfile.close()
+      raise
+    logging.debug("Setting up power monitor...")
+    self._devicename = device
+    #just in case, stop any active data collection on monsoon
+    self._dataCollectionActive = True
+    self.StopDataCollection()
+    logging.debug("Flushing input...")
+    self._FlushInput()  # discard stale input
+    logging.debug("Getting status....")
+    status = self.GetStatus()
+    
+    if not status:
+      self.log( "no response from device %s" % device)
+      self._lockfile.close()
+      raise IOError("Failed to get status from device")
+    self.start_voltage = status["voltage1"]
+    
+  def __del__(self):
+    self.Close()
+
+  def Close(self):
+    if self._logfile:
+      print("=============\n"+\
+            "Power Monitor log file can be found at '%s'"%self._logfilename +
+            "=============\n")
+      self._logfile.close()
+      self._logfile = None
+    if (self.ser):
+      #self.StopDataCollection()
+      self.ser.flush()
+      self.ser.close()
+      self.ser = None
+    if self._lockfile:
+      self._lockfile.close()
+
+  def log(self, msg , debug = False):
+    if self._logfile: self._logfile.write( msg + "\n")
+    if not debug and LOG_TO_SCREEN:
+      logging.error( msg )
+    else:
+      logging.debug(msg)
+
+  def handle_signal( self, signum, frame):
+    if self.ser:
+      self.ser.flush()
+      self.ser.close()
+      self.ser = None
+    self.log("Got signal %d"%signum)
+    sys.exit("\nGot signal %d\n"%signum)
+    
+  @staticmethod
+  def Discover():
+    monsoon_list = []
+    elapsed = 0
+    logging.info("Discovering power monitor(s)...")
+    ser_device_list = glob.glob("/dev/ttyACM*")
+    logging.info("Seeking devices %s"%ser_device_list)
+    for dev in ser_device_list:
+        try:
+            lockfile = Power_Monitor.lock( dev )
+        except:
+            logging.info( "... device %s in use, skipping"%dev)
+            continue
+        tries = 0
+        ser = None
+        while ser is None and tries < 100:
+             try:  # try to open the device
+                ser = serial.Serial( dev, timeout=TIMEOUT_SERIAL)
+             except Exception as e:
+                logging.error(  "error opening device %s: %s" % (dev, e) )
+                tries += 1
+                time.sleep(2);
+                ser = None
+        logging.info("... found device %s"%dev)
+        lockfile.close()#will be re-locked once monsoon instance created
+        logging.debug("unlocked")
+        if not ser:
+            continue
+        if ser is not None:
+            try:
+                monsoon = Power_Monitor(device = dev)
+                status = monsoon.GetStatus()
+                
+                if not status:
+                    monsoon.log("... no response from device %s, skipping")
+                    continue
+                else:
+                    logging.info("... found power monitor @ %s"%dev)
+                    monsoon_list.append( monsoon )
+            except:
+                import traceback
+                traceback.print_exc()
+                logging.error("... %s appears to not be a monsoon device"%dev)
+    logging.debug("Returning list of %s"%monsoon_list)
+    return monsoon_list
+
+  def GetStatus(self):
+    """ Requests and waits for status.  Returns status dictionary. """
+
+    # status packet format
+    self.log("Getting status...", debug = True)
+    STATUS_FORMAT = ">BBBhhhHhhhHBBBxBbHBHHHHBbbHHBBBbbbbbbbbbBH"
+    STATUS_FIELDS = [
+        "packetType", "firmwareVersion", "protocolVersion",
+        "mainFineCurrent", "usbFineCurrent", "auxFineCurrent", "voltage1",
+        "mainCoarseCurrent", "usbCoarseCurrent", "auxCoarseCurrent", "voltage2",
+        "outputVoltageSetting", "temperature", "status", "leds",
+        "mainFineResistor", "serialNumber", "sampleRate",
+        "dacCalLow", "dacCalHigh",
+        "powerUpCurrentLimit", "runTimeCurrentLimit", "powerUpTime",
+        "usbFineResistor", "auxFineResistor",
+        "initialUsbVoltage", "initialAuxVoltage",
+        "hardwareRevision", "temperatureLimit", "usbPassthroughMode",
+        "mainCoarseResistor", "usbCoarseResistor", "auxCoarseResistor",
+        "defMainFineResistor", "defUsbFineResistor", "defAuxFineResistor",
+        "defMainCoarseResistor", "defUsbCoarseResistor", "defAuxCoarseResistor",
+        "eventCode", "eventData", ]
+
+    self._SendStruct("BBB", 0x01, 0x00, 0x00)
+    while True:  # Keep reading, discarding non-status packets
+      bytes = self._ReadPacket()
+      if not bytes: return None
+      if len(bytes) != struct.calcsize(STATUS_FORMAT) or bytes[0] != "\x10":
+        self.log("wanted status, dropped type=0x%02x, len=%d" % (
+                ord(bytes[0]), len(bytes)))
+        continue
+
+      status = dict(zip(STATUS_FIELDS, struct.unpack(STATUS_FORMAT, bytes)))
+      assert status["packetType"] == 0x10
+      for k in status.keys():
+        if k.endswith("VoltageSetting"):
+          status[k] = 2.0 + status[k] * 0.01
+        elif k.endswith("FineCurrent"):
+          pass # needs calibration data
+        elif k.endswith("CoarseCurrent"):
+          pass # needs calibration data
+        elif k.startswith("voltage") or k.endswith("Voltage"):
+          status[k] = status[k] * 0.000125
+        elif k.endswith("Resistor"):
+          status[k] = 0.05 + status[k] * 0.0001
+          if k.startswith("aux") or k.startswith("defAux"): status[k] += 0.05
+        elif k.endswith("CurrentLimit"):
+          status[k] = 8 * (1023 - status[k]) / 1023.0
+      #self.log( "Returning requested status: \n %s"%(status), debug = True)
+      return status
+
+  def RampVoltage(self, start, end):
+    v = start
+    if v < 3.0: v = 3.0       # protocol doesn't support lower than this
+    while (v < end):
+      self.SetVoltage(v)
+      v += .1
+      time.sleep(.1)
+    self.SetVoltage(end)
+
+  def SetVoltage(self, v):
+    """ Set the output voltage, 0 to disable. """
+    self.log("Setting voltage to %s..."%v, debug = True)
+    if v == 0:
+      self._SendStruct("BBB", 0x01, 0x01, 0x00)
+    else:
+      self._SendStruct("BBB", 0x01, 0x01, int((v - 2.0) * 100))
+    self.log("...Set voltage", debug = True)
+
+  def SetMaxCurrent(self, i):
+    """Set the max output current."""
+    assert i >= 0 and i <= 8
+    self.log("Setting max current to %s..."%i, debug = True)
+    val = 1023 - int((i/8)*1023)
+    self._SendStruct("BBB", 0x01, 0x0a, val & 0xff)
+    self._SendStruct("BBB", 0x01, 0x0b, val >> 8)
+    self.log("...Set max current.", debug = True)
+    
+  def SetUsbPassthrough(self, val):
+    """ Set the USB passthrough mode: 0 = off, 1 = on,  2 = auto. """
+    self._SendStruct("BBB", 0x01, 0x10, val)
+
+  def StartDataCollection(self):    
+    """ Tell the device to start collecting and sending measurement data. """
+    self.log("Starting data collection...", debug = True)
+    self._SendStruct("BBB", 0x01, 0x1b, 0x01) # Mystery command
+    self._SendStruct("BBBBBBB", 0x02, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8)
+    self.log("...started", debug = True)
+    self._dataCollectionActive = True
+    
+  def StopDataCollection(self):
+    """ Tell the device to stop collecting measurement data. """
+    self._SendStruct("BB", 0x03, 0x00) # stop
+    if self._dataCollectionActive:
+      while self.CollectData(False) is not None:
+        pass
+    self._dataCollectionActive = False
+    
+  def CollectData(self, verbose = True):
+    """ Return some current samples.  Call StartDataCollection() first. """
+    #self.log("Collecting data ...", debug = True)
+    while True:  # loop until we get data or a timeout
+      bytes = self._ReadPacket(verbose)
+      
+      if not bytes: return None
+      if len(bytes) < 4 + 8 + 1 or bytes[0] < "\x20" or bytes[0] > "\x2F":
+        if verbose: self.log( "wanted data, dropped type=0x%02x, len=%d" % (
+          ord(bytes[0]), len(bytes)), debug=verbose)
+        continue
+
+      seq, type, x, y = struct.unpack("BBBB", bytes[:4])
+      data = [struct.unpack(">hhhh", bytes[x:x+8])
+              for x in range(4, len(bytes) - 8, 8)]
+
+      if self._last_seq and seq & 0xF != (self._last_seq + 1) & 0xF:
+        self.log( "data sequence skipped, lost packet?" )
+      self._last_seq = seq
+
+      if type == 0:
+        if not self._coarse_scale or not self._fine_scale:
+          self.log("waiting for calibration, dropped data packet")
+          continue
+
+        out = []
+        for main, usb, aux, voltage in data:
+          if main & 1:
+            out.append(((main & ~1) - self._coarse_zero) * self._coarse_scale)
+          else:
+            out.append((main - self._fine_zero) * self._fine_scale)
+        #self.log("...Collected %d samples"%(len(out)), debug = True)
+        return out
+
+      elif type == 1:
+        self._fine_zero = data[0][0]
+        self._coarse_zero = data[1][0]
+
+      elif type == 2:
+        self._fine_ref = data[0][0]
+        self._coarse_ref = data[1][0]
+
+      else:
+        self.log( "discarding data packet type=0x%02x" % type)
+        continue
+
+      if self._coarse_ref != self._coarse_zero:
+        self._coarse_scale = 2.88 / (self._coarse_ref - self._coarse_zero)
+      if self._fine_ref != self._fine_zero:
+        self._fine_scale = 0.0332 / (self._fine_ref - self._fine_zero)
+
+
+  def _SendStruct(self, fmt, *args):
+    """ Pack a struct (without length or checksum) and send it. """
+    data = struct.pack(fmt, *args)
+    data_len = len(data) + 1
+    checksum = (data_len + sum(struct.unpack("B" * len(data), data))) % 256
+    out = struct.pack("B", data_len) + data + struct.pack("B", checksum)
+    self.ser.write(out)
+    self.ser.flush()
+
+  def _ReadPacket(self, verbose = True):
+    """ Read a single data record as a string (without length or checksum). """
+    len_char = self.ser.read(1)
+    if not len_char:
+      if verbose: self.log( "timeout reading from serial port" )
+      return None
+
+    data_len = struct.unpack("B", len_char)
+    data_len = ord(len_char)
+    if not data_len: return ""
+
+    result = self.ser.read(data_len)
+    if len(result) != data_len: return None
+    body = result[:-1]
+    checksum = (data_len + sum(struct.unpack("B" * len(body), body))) % 256
+    if result[-1] != struct.pack("B", checksum):
+      self.log( "Invalid checksum from serial port" )
+      return None
+    return result[:-1]
+
+  def _FlushInput(self):
+    """ Flush all read data until no more available. """
+    self.ser.flushInput()
+    flushed = 0
+    self.log("Flushing input...", debug = True)
+    while True:
+      ready_r, ready_w, ready_x = select.select([self.ser], [], [self.ser], 0)
+      if len(ready_x) > 0:
+        self.log( "exception from serial port" )
+        return None
+      elif len(ready_r) > 0:
+        flushed += 1
+        self.ser.read(1)  # This may cause underlying buffering.
+        self.ser.flush()  # Flush the underlying buffer too.
+      else:
+        break
+    if flushed > 0:
+      self.log( "flushed >%d bytes" % flushed, debug = True )
+
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 5683391..457b54e 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -612,6 +612,7 @@
 
     <!-- Strings for USB accessory test activity -->
     <string name="usb_accessory_test">USB Accessory Test</string>
+    <string name="sensor_power_test">Sensor Power Test</string>
     <string name="usb_accessory_test_info">
         1. Connect your Android device to a computer and run the \'cts-usb-accessory\' program
         included with the CTS Verifier bundle.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java
index f63c5b7..ae479d4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java
@@ -158,7 +158,9 @@
         mCountDownLatch.countDown();
     }
 
-    protected enum SensorTestResult {
+    // TODO: this should be protected currently it is used by power tests, but the result should
+    // only be available in this class
+    public enum SensorTestResult {
         SKIPPED,
         PASS,
         FAIL
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorPowerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorPowerTestActivity.java
new file mode 100644
index 0000000..97901ac
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorPowerTestActivity.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.sensors;
+
+import com.android.cts.verifier.sensors.helpers.PowerTestHostLink;
+
+import junit.framework.Assert;
+
+public class SensorPowerTestActivity extends BaseSensorTestActivity implements
+        PowerTestHostLink.HostToDeviceInterface {
+
+    public class TestExecutionException extends Exception {
+        public TestExecutionException(final String message) {
+            super(message);
+        }
+    }
+
+    public SensorPowerTestActivity() {
+        super(SensorPowerTestActivity.class);
+    }
+
+    private String TAG = "SensorPowerTestActivity";
+    private PowerTestHostLink mHostLink;
+
+    /** HostToDeviceInterface implementation **/
+    public void waitForUserAcknowledgement(final String message) {
+        appendText(message);
+        waitForUser();
+    }
+
+    /* channel for host to raise an exception on the device if needed */
+    public void raiseError(final String testname,
+            final String message) throws Exception {
+        setTestResult(testname, SensorTestResult.SKIPPED, message);
+        throw new TestExecutionException(message);
+    }
+
+    public void logText(String text) {
+        appendText(text);
+    }
+
+    public void logTestResult(String testId, SensorTestResult testResult, String testDetails) {
+        setTestResult(testId, testResult, testDetails);
+    }
+
+    public String testSensorsPower() throws Throwable {
+        String testDetails = "";
+        if (mHostLink == null) {
+            // prepare Activity screen to show instructions to the operator
+            clearText();
+
+            // test setup, make sure the device is in the correct state before
+            // executing the scenarios
+            askToSetAirplaneMode();
+            askToSetScreenOffTimeout(15 /* seconds */);
+
+            // ask the operator to set up the host
+            appendText("Connect the device to the host machine.");
+            appendText("Execute the following script (the command is available in CtsVerifier.zip):");
+            appendText("    # python power/execute_power_tests.py --power_monitor <implementation> --run");
+            appendText("where \"<implementation>\" is the power monitor implementation being used, for example \"monsoon\"");
+            try {
+                mHostLink = new PowerTestHostLink(this, this);
+
+                appendText("Waiting for connection from Host...");
+
+                // this will block until first connection from host,
+                // and then allow the host to execute tests one by on
+                // until it issues an "EXIT" command to break out
+                // of the run loop. The host will run all associated tests
+                // sequentially here:
+                final PowerTestHostLink.PowerTestResult testResult = mHostLink.run();
+                testDetails = testResult.testDetails;
+                Assert.assertEquals(testDetails, 0, testResult.failedCount );
+            } finally {
+                mHostLink.close();
+                mHostLink = null;
+            }
+
+        } else {
+            throw new IllegalStateException("Attempt to run test twice");            
+        }
+        return testDetails;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
new file mode 100644
index 0000000..f8b7dda
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.sensors.helpers;
+
+import com.android.cts.verifier.sensors.BaseSensorTestActivity.SensorTestResult;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/*
+ * This class handles communication with the host to respond to commands
+ * The command/response link is through a TCP socket on the host side,
+ * forwarded via adb to a local socket on the device.  The system uses a 
+ * standard "accept-read_command-send_response-close" to execute commands
+ * sent from the host.  
+ * 
+ * CAUTION: The local socket name (SOCKET_NAME below) must match that used by the host
+ * to set up the adb-forwarding.
+ */
+public class PowerTestHostLink {
+
+    /*
+     * Host-to-device bridge will use a Listener instance to drive the test via
+     * the CtsVerifier running on the device.
+     */
+    public interface HostToDeviceInterface {
+
+        public void logTestResult(final String testName,
+                final SensorTestResult result,
+                final String message);
+
+        public void raiseError(final String testName, final String message) throws Exception;
+
+        public void waitForUserAcknowledgement(final String message);
+
+        public void logText(String text);
+
+    };
+    
+    /** This is a data-only message to communicate result of a power test **/
+    public class PowerTestResult{
+        public int passedCount = 0;
+        public int skippedCount = 0;
+        public int failedCount = 0;
+        public String testDetails = "";
+    };
+   
+
+    public final String TAG = "PowerTestHostLink";
+
+    /**
+     * Standard response types back to host. Host-side code must match these
+     * definitions
+     */
+    private final static String RESPONSE_OK = "OK";
+    private final static String RESPONSE_ERR = "ERR";
+    private final static String RESPONSE_UNAVAILABLE = "UNAVAILABLE";
+
+    /**
+     * Socket name for host adb forwarded communications. Must match naem in
+     * host-side code
+     */
+    public final static String SOCKET_NAME = "/android/cts/powertest";
+
+    private LocalServerSocket mServerSocket;
+    private volatile boolean mStopThread;
+    private final SensorManager mSensorManager;
+    private final PowerManager mPowerManager;
+    private final Context mContext;
+    private final HostToDeviceInterface mHostToDeviceExecutor;
+    private PowerTestResult mTestResult;
+    private StringBuilder mStringBuilder;
+
+    public PowerTestHostLink(Context context, final HostToDeviceInterface listener) {
+        Log.d(TAG, " +++ Begin of localSocketServer() +++ ");
+        mHostToDeviceExecutor = listener;
+        mContext = context;
+        try {
+            mServerSocket = new LocalServerSocket(SOCKET_NAME);
+            Log.i(TAG, "OKAY");
+
+        } catch (IOException e) {
+            Log.e(TAG, "The local Socket Server create failed");
+            e.printStackTrace();
+        }
+        if (mServerSocket != null) {
+            Log.d(TAG, "Bound to local server socket");
+        } else {
+            Log.e(TAG, "Unable to bind to local socket ");
+        }
+        mStopThread = false;
+
+        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+    }
+
+    /**
+     * Ensure connection to host is closed; stop accepting requests
+     **/
+    public void close() {
+        mStopThread = true;
+    }
+
+
+    
+    /**
+     * Run the suite of tests via the host, responding to host requests
+     * 
+     * @return number of failed test cases
+     * @throws Exception
+     */
+    public PowerTestResult run() throws Exception {
+        mTestResult = new PowerTestResult();
+        mStringBuilder = new StringBuilder();
+        // define buffer to receive data from host
+        final int BUFFER_SIZE = 4096;
+        byte[] buffer = new byte[BUFFER_SIZE];
+
+        if (null == mServerSocket) {
+            Log.d(TAG, "The localSocketServer is NULL !!!");
+            mStopThread = true;
+        }
+        InputStream streamIn;
+        OutputStream streamOut;
+        LocalSocket receiverSocket;
+        while (!mStopThread) {
+
+            try {
+                Log.d(TAG, "localSocketServer accept...");
+                receiverSocket = mServerSocket.accept();
+                Log.d(TAG, "Got new connection");
+            } catch (IOException e) {
+                Log.d(TAG, "localSocketServer accept() failed !!!", e);
+                continue;
+            }
+
+            try {
+                streamIn = receiverSocket.getInputStream();
+            } catch (IOException e) {
+                Log.d(TAG, "getInputStream() failed !!!", e);
+                continue;
+            }
+
+            try {
+                streamOut = receiverSocket.getOutputStream();
+            } catch (IOException e) {
+                Log.e(TAG, "getOutputStream() failed", e);
+                continue;
+            }
+
+            Log.d(TAG, "The client connected to LocalServerSocket");
+
+            try {
+                int total = 0;
+                // command and response handshake, so read all data
+                while (streamIn.available() > 0 || total == 0) {
+                    if (total < BUFFER_SIZE) {
+                        int bytesRead = streamIn.read(buffer, total,
+                                (BUFFER_SIZE - total));
+                        if (bytesRead > 0) {
+                            total += bytesRead;
+                        }
+                    } else {
+                        Log.e(TAG, "Message too long: truncating");
+                    }
+                }
+                String clientRequest = new String(buffer);
+                clientRequest = clientRequest.substring(0, total);
+                if (clientRequest.length() > 0) {
+
+                    Log.d(TAG, "Client requested: " + clientRequest);
+                    try {
+                        String response = processClientRequest(clientRequest);
+                        if (response != null) {
+                            Log.d(TAG, "Sending response " + response);
+                            streamOut.write(response.getBytes(), 0, response.length());
+                        }
+                        // null response means response is defered awaiting user
+                        // response
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error executing " + clientRequest, e);
+                        streamOut.write(RESPONSE_ERR.getBytes(), 0, RESPONSE_ERR.length());
+                    }
+                }
+                receiverSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "There is an exception when reading from or writing tosocket", e);
+                break;
+            }
+        }
+        Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
+
+        if (mServerSocket != null) {
+            try {
+                mServerSocket.close();
+            } catch (IOException e) {
+                Log.d(TAG, "Exception on close of server socket", e);
+            }
+        }
+        mHostToDeviceExecutor.logText("Device disconnected.");
+        if (mStringBuilder!=null){
+            mTestResult.testDetails = mStringBuilder.toString();
+        }
+        Log.d(TAG, "Returning " + mTestResult.passedCount + "passed " + mTestResult.skippedCount + "skipped " +
+        mTestResult.failedCount + "failed :" + mTestResult.testDetails);
+        return mTestResult;
+    }
+    
+
+    protected String processClientRequest(String request) throws SettingNotFoundException,
+            Exception {
+        final String USER_REQUEST = "REQUEST USER RESPONSE";
+        String response = RESPONSE_ERR;
+        // Queries must appear first and then commands to direct actions after
+        // in this if/else construct
+        if (request.startsWith("SCREEN OFF TIMEOUT?")) {
+            int timeout = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.SCREEN_OFF_TIMEOUT);
+            response = "" + timeout;
+        } else if (request.startsWith("AIRPLANE MODE ON?")) {
+            boolean airplaneModeOn = Settings.Global.getInt
+                    (mContext.getContentResolver(),
+                            Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+            response = airplaneModeOn ? RESPONSE_OK : RESPONSE_ERR;
+        } else if (request.startsWith("SENSOR?")) {
+            final String sensor = request.substring(9);
+            final int sensorId = getSensorId(sensor);
+            if (mSensorManager.getDefaultSensor(sensorId) == null) {
+                response = RESPONSE_UNAVAILABLE;
+            } else {
+                response = RESPONSE_OK;
+            }
+        } else if (request.startsWith("EXTERNAL STORAGE?")){
+            response = System.getenv("EXTERNAL_STORAGE");
+            Log.d(TAG,"External storage is " + response);
+        }else if (request.startsWith("SCREEN OFF?")) {        
+            boolean screenOn = mPowerManager.isScreenOn();
+            response = screenOn ? RESPONSE_ERR : RESPONSE_OK;
+        } else if (request.startsWith("SCREEN ON?")) {
+            boolean screenOn = mPowerManager.isScreenOn();
+            response = screenOn ? RESPONSE_OK : RESPONSE_ERR;
+        } else if (request.startsWith("SENSOR ON ")) {
+            String sensorList = request.substring(10).trim();
+            response = handleSensorSensorSwitchCmd(sensorList, true);
+        } else if (request.startsWith("SENSOR OFF")) {
+            String sensorList = request.substring(10).trim();
+            response = handleSensorSensorSwitchCmd(sensorList, false);
+        } else if (request.startsWith("MESSAGE")) {
+            final String message = request.substring(8);
+            mHostToDeviceExecutor.logText(message);
+            response = RESPONSE_OK;
+        } else if (request.startsWith(USER_REQUEST)) {
+            final String message = request.substring(USER_REQUEST.length() + 1);
+            mHostToDeviceExecutor.waitForUserAcknowledgement(message);
+            response = RESPONSE_OK;
+        } else if (request.startsWith("SET TEST RESULT")) {
+            response = handleSetTestResultCmd(request);
+        } else if (request.startsWith("RAISE")) {
+            StringTokenizer tokenizer = new StringTokenizer(request);
+            try {
+                tokenizer.nextToken();/* RAISE */
+                final String testName = tokenizer.nextToken();
+                final String message = request.substring(7 + testName.length());
+                mHostToDeviceExecutor.raiseError(testName, message);
+                response = RESPONSE_OK;
+            } catch (Exception e) {
+                Log.e(TAG, "Invalid RAISE command received (bad arguments): " + request);
+                response = RESPONSE_ERR;
+            }
+        } else if (request.startsWith("EXIT")) {
+            mStopThread = true;
+            response = RESPONSE_OK;
+        } else {
+            Log.e(TAG, "Unknown request: " + request);
+        }
+        return response;
+    }
+
+    protected String getCurrentTime() {
+        return DateFormat.getDateTimeInstance().format(new Date());
+    }
+
+    protected String handleSetTestResultCmd(final String request) {
+        String response = RESPONSE_OK;
+        StringTokenizer tokenizer = new StringTokenizer(request, " ");
+        String testName = "";
+        SensorTestResult result = SensorTestResult.FAIL;
+        String message = "";
+
+        try {
+            tokenizer.nextToken();/* SET */
+            tokenizer.nextToken();/* TEST */
+            tokenizer.nextToken();/* RESULT */
+            testName = tokenizer.nextToken();
+            final String resultToken = tokenizer.nextToken();
+
+            if (resultToken.equals("PASS")) {
+                result = SensorTestResult.PASS;
+                ++mTestResult.passedCount;
+                message = "PASSED: ";
+                response = RESPONSE_OK;
+            } else if (resultToken.equals("FAIL")) {
+                result = SensorTestResult.FAIL;
+                ++mTestResult.failedCount;
+                message = "FAILED: ";
+                response = RESPONSE_OK;
+            } else if (resultToken.equals("SKIPPED")) {
+                result = SensorTestResult.SKIPPED;
+                ++mTestResult.skippedCount;
+                message = "SKIPPED: ";
+                response = RESPONSE_OK;
+            } else {
+                response = RESPONSE_ERR;
+            }
+            while (tokenizer.hasMoreTokens()) {
+                message += tokenizer.nextToken() + " ";
+            }
+            Log.i(TAG, message);
+        } catch (Exception e) {
+            Log.e(TAG, "Improperly formatted command received: " + request, e);
+            response = RESPONSE_ERR;
+        }
+        String fullMessage = testName + " " + message;
+        mStringBuilder.append(fullMessage + "\n");
+        mHostToDeviceExecutor.logTestResult(testName, result, fullMessage );
+        return response;
+    }
+
+    protected String handleSensorSensorSwitchCmd(String sensorList, boolean switchOn) {
+        String response = RESPONSE_ERR;
+        try {
+            StringTokenizer tokenizer = new StringTokenizer(sensorList, " ");
+            int n = tokenizer.countTokens();
+            if (n == 0) {
+                response = switchAllSensors(switchOn);
+            } else {
+                String sensorName = tokenizer.nextToken();
+                String requestRate = "";
+                if (n > 1) {
+                    requestRate = tokenizer.nextToken();
+                }
+                if (sensorName.equals("ALL")) {
+                    response = switchAllSensors(switchOn);
+                } else {
+                    int sensorId = getSensorId(sensorName);
+                    if (sensorId >= 0) {
+                        response = switchSensor(sensorId, switchOn, requestRate);
+                    } else {
+                        Log.e(TAG, "Unknown sensor in request: " + sensorName);
+                        response = RESPONSE_UNAVAILABLE;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Improperly formatted command received on setting sensor state");
+            response = RESPONSE_ERR;
+        }
+        return response;
+    }
+
+    protected int getSensorId(String sensorName) {
+        int sensorId = -1;
+
+        if (sensorName.compareToIgnoreCase("ACCELEROMETER") == 0) {
+            sensorId = Sensor.TYPE_ACCELEROMETER;
+        } else if (sensorName.compareToIgnoreCase("AMBIENT_TEMPERATURE") == 0) {
+            sensorId = Sensor.TYPE_AMBIENT_TEMPERATURE;
+        } else if (sensorName.compareToIgnoreCase("GAME_ROTATION_VECTOR") == 0) {
+            sensorId = Sensor.TYPE_GAME_ROTATION_VECTOR;
+        } else if (sensorName.compareToIgnoreCase("GEOMAGNETIC_ROTATION_VECTOR") == 0) {
+            sensorId = Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR;
+        } else if (sensorName.compareToIgnoreCase("GRAVITY") == 0) {
+            sensorId = Sensor.TYPE_GRAVITY;
+        } else if (sensorName.compareToIgnoreCase("GYROSCOPE") == 0) {
+            sensorId = Sensor.TYPE_GYROSCOPE;
+        } else if (sensorName.compareToIgnoreCase("LIGHT") == 0) {
+            sensorId = Sensor.TYPE_LIGHT;
+        } else if (sensorName.compareToIgnoreCase("MAGNETIC_FIELD") == 0) {
+            sensorId = Sensor.TYPE_MAGNETIC_FIELD;
+        } else if (sensorName.compareToIgnoreCase("PRESSURE") == 0) {
+            sensorId = Sensor.TYPE_PRESSURE;
+        } else if (sensorName.compareToIgnoreCase("PROXIMITY") == 0) {
+            sensorId = Sensor.TYPE_PROXIMITY;
+        } else if (sensorName.compareToIgnoreCase("RELATIVE_HUMIDITY") == 0) {
+            sensorId = Sensor.TYPE_RELATIVE_HUMIDITY;
+        } else if (sensorName.compareToIgnoreCase("ROTATION_VECTOR") == 0) {
+            sensorId = Sensor.TYPE_ROTATION_VECTOR;
+        } else if (sensorName.compareToIgnoreCase("SIGNIFICANT_MOTION") == 0) {
+            sensorId = Sensor.TYPE_SIGNIFICANT_MOTION;
+        } else if (sensorName.compareToIgnoreCase("STEP_COUNTER") == 0) {
+            sensorId = Sensor.TYPE_STEP_COUNTER;
+        } else if (sensorName.compareToIgnoreCase("STEP_DETECTOR") == 0) {
+            sensorId = Sensor.TYPE_STEP_DETECTOR;
+        }
+
+        return sensorId;
+    }
+
+    protected String switchSensor(int sensorId, boolean switchOn) {
+        return switchSensor(sensorId, switchOn, "SENSOR_DELAY_NORMAL");
+    }
+
+    protected String switchSensor(int sensorId, boolean switchOn, String requestFrequency) {
+        String response = RESPONSE_ERR;
+        int rateUs = SensorManager.SENSOR_DELAY_NORMAL;
+
+        if (requestFrequency.compareToIgnoreCase("SENSOR_DELAY_FASTEST") == 0) {
+            rateUs = SensorManager.SENSOR_DELAY_FASTEST;
+        } else if (requestFrequency.compareToIgnoreCase("SENSOR_DELAY_GAME") == 0) {
+            rateUs = SensorManager.SENSOR_DELAY_GAME;
+        } else if (requestFrequency.compareToIgnoreCase("SENSOR_DELAY_UI") == 0) {
+            rateUs = SensorManager.SENSOR_DELAY_UI;
+        }
+
+        if (switchOn) {
+            mSensorManager.registerListener(mSensorEventListener,
+                    mSensorManager.getDefaultSensor(sensorId), rateUs);
+            response = RESPONSE_OK;
+            Log.v(TAG, "Switching ON " + String.valueOf(sensorId) + " | " + String.valueOf(rateUs));
+        } else {
+            mSensorManager.unregisterListener(mSensorEventListener,
+                    mSensorManager.getDefaultSensor(sensorId));
+            response = RESPONSE_OK;
+            Log.v(TAG, "Switching  OFF " + String.valueOf(sensorId));
+        }
+
+        return response;
+    }
+
+    protected String switchAllSensors(boolean on) {
+        List<Sensor> allSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        String response = RESPONSE_OK;
+        for (Sensor sensor : allSensors) {
+            response = switchSensor(sensor.getType(), on);
+            if (response == null) {
+                response = RESPONSE_ERR;
+            }
+        }
+        return response;
+    }
+
+    private SensorEventListener mSensorEventListener = new SensorEventListener() {
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+        }
+    };
+
+}