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) {
+ }
+ };
+
+}