1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.sensors; 18 19 import com.android.cts.verifier.R; 20 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity; 21 22 import junit.framework.Assert; 23 24 import android.content.Context; 25 import android.hardware.Sensor; 26 import android.hardware.SensorEvent; 27 import android.hardware.SensorEventListener; 28 import android.hardware.SensorManager; 29 import android.hardware.cts.helpers.MovementDetectorHelper; 30 import android.hardware.cts.helpers.SensorTestStateNotSupportedException; 31 import android.hardware.cts.helpers.TestSensorEnvironment; 32 import android.hardware.cts.helpers.TestSensorEvent; 33 import android.os.SystemClock; 34 import android.view.MotionEvent; 35 import android.view.View; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.concurrent.TimeUnit; 40 41 public class StepCounterTestActivity 42 extends SensorCtsVerifierTestActivity 43 implements SensorEventListener { 44 public StepCounterTestActivity() { 45 super(StepCounterTestActivity.class); 46 } 47 48 private static final int TEST_DURATION_SECONDS = 20; 49 private static final long TIMESTAMP_SYNCHRONIZATION_THRESHOLD_NANOS = 50 TimeUnit.MILLISECONDS.toNanos(500); 51 52 private static final int MIN_NUM_STEPS_PER_TEST = 10; 53 private static final int MAX_STEP_DISCREPANCY = 5; 54 private static final long MAX_TOLERANCE_STEP_TIME_NANOS = TimeUnit.SECONDS.toNanos(10); 55 56 private static final long[] VIBRATE_PATTERN = { 57 1000L, 500L, 1000L, 750L, 1000L, 500L, 1000L, 750L, 1000L, 1000L, 500L, 1000L, 58 750L, 1000L, 500L, 1000L }; 59 60 private SensorManager mSensorManager; 61 private Sensor mSensorStepCounter; 62 private Sensor mSensorStepDetector; 63 private MovementDetectorHelper mMovementDetectorHelper; 64 65 private volatile boolean mMoveDetected; 66 67 private final List<Long> mTimestampsUserReported = new ArrayList<Long>(); 68 private final List<TestSensorEvent> mStepCounterEvents = new ArrayList<TestSensorEvent>(); 69 private final List<TestSensorEvent> mStepDetectorEvents = new ArrayList<TestSensorEvent>(); 70 71 /** 72 * A flag that indicates if the test is interested in registering steps reported by the 73 * operator. The registration of events happens by tapping the screen throughout the test. 74 */ 75 private volatile boolean mCheckForMotion; 76 77 @Override 78 protected void activitySetUp() { 79 mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); 80 mSensorStepCounter = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); 81 mSensorStepDetector = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR); 82 83 if (mSensorStepCounter == null && mSensorStepDetector == null) { 84 throw new SensorTestStateNotSupportedException( 85 "Sensors Step Counter/Detector are not supported."); 86 } 87 88 setLogScrollViewListener(new View.OnTouchListener() { 89 @Override 90 public boolean onTouch(View v, MotionEvent event) { 91 // during movement of the device, the ScrollView will detect user taps as attempts 92 // to scroll, when in reality they are taps in the layout 93 // to overcome the fact that a ScrollView cannot be disabled from scrolling, we 94 // listen for ACTION_UP events instead of click events in the child layout 95 long elapsedTime = SystemClock.elapsedRealtimeNanos(); 96 if (event.getAction() != MotionEvent.ACTION_UP) { 97 return false; 98 } 99 100 try { 101 logUserReportedStep(elapsedTime); 102 } catch (InterruptedException e) { 103 // we cannot propagate the exception in the main thread, so we just catch and 104 // restore the status, we don't need to log as we are terminating anyways 105 Thread.currentThread().interrupt(); 106 } 107 return false; 108 } 109 }); 110 } 111 112 @Override 113 protected void activityCleanUp() { 114 setLogScrollViewListener(null /* listener */); 115 } 116 117 public String testWalking() throws Throwable { 118 return runTest( 119 R.string.snsr_step_counter_test_walking, 120 MIN_NUM_STEPS_PER_TEST, 121 false /* vibrate */); 122 } 123 124 public String testStill() throws Throwable { 125 return runTest( 126 R.string.snsr_step_counter_test_still, 127 0 /* expectedSteps */, 128 true /* vibrate */); 129 } 130 131 /** 132 * @param instructionsResId Resource ID containing instruction to be shown to testers 133 * @param expectedSteps Number of steps expected in this test 134 * @param vibrate If TRUE, vibration will be concurrent with the test 135 */ 136 private String runTest(int instructionsResId, int expectedSteps, boolean vibrate) 137 throws Throwable { 138 mTimestampsUserReported.clear(); 139 mStepCounterEvents.clear(); 140 mStepDetectorEvents.clear(); 141 142 mMoveDetected = false; 143 mCheckForMotion = false; 144 145 getTestLogger().logInstructions(instructionsResId); 146 waitForUserToBegin(); 147 148 mCheckForMotion = (expectedSteps > 0); 149 if (vibrate) { 150 vibrate(VIBRATE_PATTERN); 151 } 152 startMeasurements(); 153 getTestLogger().logWaitForSound(); 154 155 Thread.sleep(TimeUnit.SECONDS.toMillis(TEST_DURATION_SECONDS)); 156 mCheckForMotion = false; 157 playSound(); 158 159 return verifyMeasurements(expectedSteps); 160 } 161 162 private void startMeasurements() { 163 if (mSensorStepCounter != null) { 164 mSensorManager.registerListener(this, mSensorStepCounter, 165 SensorManager.SENSOR_DELAY_NORMAL); 166 } 167 168 if (mSensorStepDetector != null) { 169 mSensorManager.registerListener(this, mSensorStepDetector, 170 SensorManager.SENSOR_DELAY_NORMAL); 171 } 172 173 mMovementDetectorHelper = new MovementDetectorHelper(getApplicationContext()) { 174 @Override 175 protected void onMovementDetected() { 176 mMoveDetected = true; 177 } 178 }; 179 mMovementDetectorHelper.start(); 180 } 181 182 private String verifyMeasurements(int stepsExpected) { 183 mSensorManager.unregisterListener(this); 184 mMovementDetectorHelper.stop(); 185 186 if (mCheckForMotion) { 187 Assert.assertTrue( 188 getString(R.string.snsr_movement_expected, mMoveDetected), 189 mMoveDetected); 190 } 191 192 final int userReportedSteps = mTimestampsUserReported.size(); 193 String stepsReportedMessage = getString( 194 R.string.snsr_step_counter_expected_steps, 195 stepsExpected, 196 userReportedSteps); 197 Assert.assertFalse(stepsReportedMessage, userReportedSteps < stepsExpected); 198 199 // TODO: split test cases for step detector and counter 200 verifyStepDetectorMeasurements(); 201 verifyStepCounterMeasurements(); 202 return null; 203 } 204 205 private void verifyStepCounterMeasurements() { 206 if (mSensorStepCounter == null) { 207 // sensor not supported, so no-op 208 return; 209 } 210 211 final int userReportedSteps = mTimestampsUserReported.size(); 212 int totalStepsCounted = 0; 213 int initialStepCount = -1; 214 for (TestSensorEvent counterEvent : mStepCounterEvents) { 215 String sensorName = counterEvent.sensor.getName(); 216 float[] values = counterEvent.values; 217 218 final int expectedLength = 1; 219 int valuesLength = values.length; 220 String eventLengthMessage = getString( 221 R.string.snsr_event_length, 222 expectedLength, 223 valuesLength, 224 sensorName); 225 Assert.assertEquals(eventLengthMessage, expectedLength, valuesLength); 226 227 int stepValue = (int) values[0]; 228 if (initialStepCount == -1) { 229 initialStepCount = stepValue; 230 } else { 231 int stepsCounted = stepValue - initialStepCount; 232 int countDelta = stepsCounted - totalStepsCounted; 233 234 String eventTriggered = getString( 235 R.string.snsr_step_counter_event_changed, 236 countDelta, 237 counterEvent.timestamp); 238 Assert.assertTrue(eventTriggered, countDelta > 0); 239 240 // TODO: abstract this into an ISensorVerification 241 242 long deltaThreshold = TIMESTAMP_SYNCHRONIZATION_THRESHOLD_NANOS 243 + TestSensorEnvironment.getSensorMaxDetectionLatencyNs(counterEvent.sensor); 244 assertTimestampSynchronization( 245 counterEvent.timestamp, 246 counterEvent.receivedTimestamp, 247 deltaThreshold, 248 counterEvent.sensor.getName()); 249 250 totalStepsCounted = stepsCounted; 251 } 252 } 253 254 int stepsCountedDelta = Math.abs(totalStepsCounted - userReportedSteps); 255 String stepsDeltaMessage = getString( 256 R.string.snsr_step_counter_detected_reported, 257 userReportedSteps, 258 totalStepsCounted, 259 stepsCountedDelta, 260 MAX_STEP_DISCREPANCY); 261 Assert.assertFalse(stepsDeltaMessage, stepsCountedDelta > MAX_STEP_DISCREPANCY); 262 263 int stepCounterLength = mStepCounterEvents.size(); 264 for (int i = 0; i < userReportedSteps && i < stepCounterLength; ++i) { 265 long userReportedTimestamp = mTimestampsUserReported.get(i); 266 TestSensorEvent counterEvent = mStepCounterEvents.get(i); 267 268 assertTimestampSynchronization( 269 counterEvent.timestamp, 270 userReportedTimestamp, 271 MAX_TOLERANCE_STEP_TIME_NANOS, 272 counterEvent.sensor.getName()); 273 } 274 } 275 276 private void verifyStepDetectorMeasurements() { 277 if (mSensorStepDetector == null) { 278 // sensor not supported, so no-op 279 return; 280 } 281 282 final int userReportedSteps = mTimestampsUserReported.size(); 283 int stepsDetected = mStepDetectorEvents.size(); 284 int stepsDetectedDelta = Math.abs(stepsDetected - userReportedSteps); 285 String stepsDetectedMessage = getString( 286 R.string.snsr_step_detector_detected_reported, 287 userReportedSteps, 288 stepsDetected, 289 stepsDetectedDelta, 290 MAX_STEP_DISCREPANCY); 291 Assert.assertFalse(stepsDetectedMessage, stepsDetectedDelta > MAX_STEP_DISCREPANCY); 292 293 for (TestSensorEvent detectorEvent : mStepDetectorEvents) { 294 String sensorName = detectorEvent.sensor.getName(); 295 float[] values = detectorEvent.values; 296 297 final int expectedLength = 1; 298 int valuesLength = values.length; 299 String eventLengthMessage = getString( 300 R.string.snsr_event_length, 301 expectedLength, 302 valuesLength, 303 sensorName); 304 Assert.assertEquals(eventLengthMessage, expectedLength, valuesLength); 305 306 final float expectedValue = 1.0f; 307 float value0 = values[0]; 308 String eventValueMessage = 309 getString(R.string.snsr_event_value, expectedValue, value0, sensorName); 310 Assert.assertEquals(eventValueMessage, expectedValue, value0); 311 } 312 313 // TODO: verify correlation of events with steps from user 314 } 315 316 @Override 317 public final void onAccuracyChanged(Sensor sensor, int accuracy) { 318 } 319 320 public final void onSensorChanged(SensorEvent event) { 321 long elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); 322 int type = event.sensor.getType(); 323 if (type == Sensor.TYPE_STEP_COUNTER) { 324 mStepCounterEvents.add(new TestSensorEvent(event, elapsedRealtimeNanos)); 325 getTestLogger().logMessage( 326 R.string.snsr_step_counter_event, 327 elapsedRealtimeNanos, 328 (int) event.values[0]); 329 } else if (type == Sensor.TYPE_STEP_DETECTOR) { 330 mStepDetectorEvents.add(new TestSensorEvent(event, elapsedRealtimeNanos)); 331 getTestLogger().logMessage(R.string.snsr_step_detector_event, elapsedRealtimeNanos); 332 333 } 334 // TODO: with delayed assertions check events of other types are tracked 335 } 336 337 private void logUserReportedStep(long timestamp) throws InterruptedException { 338 if (!mCheckForMotion) { 339 return; 340 } 341 playSound(); 342 mTimestampsUserReported.add(timestamp); 343 getTestLogger().logMessage(R.string.snsr_step_reported, timestamp); 344 } 345 } 346