Home | History | Annotate | Download | only in sensors
      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