blob: 0dfe34196775dc9c1109cac6eb1799d67c782be4 [file] [log] [blame]
/*
* 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 java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.cts.helpers.SensorCtsHelper;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Build;
import android.os.SystemClock;
import android.os.Vibrator;
import android.view.View;
import android.view.View.OnClickListener;
import com.android.cts.verifier.R;
@TargetApi(Build.VERSION_CODES.KITKAT)
public class StepCounterTestActivity extends BaseSensorSemiAutomatedTestActivity
implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mSensorStepCounter;
private Sensor mSensorStepDetector;
private int mStepsReported = 0; // number of steps as reported by user
private int mInitialStepCount = 0; // step counter at the start of test
private int mStepsDetected = 0; // number of steps during the test
private List<Long> mTimestampsUserReported = new ArrayList<Long>();
private List<Long> mTimestampsStepCounter = new ArrayList<Long>();
private List<Long> mTimestampsStepDetector = new ArrayList<Long>();
private final int MIN_TEST_TIME_MILLIS = 20000; // 20 sec
private final double NANOSECONDS_IN_SEC = 1e9;
private final int MIN_NUM_STEPS_PER_TEST = 10;
private final int MAX_STEP_DISCREPANCY = 4;
private final int MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS = 8;
private boolean mCheckForMotion = false;
private Sensor mSensorAcceleration;
private boolean mMoveDetected = false;
private static int sNumPassedTests = 0;
@Override
protected void onRun() throws Throwable {
View screen = (View) findViewById(R.id.log_text).getParent();
Assert.assertNotNull(screen);
screen.setOnClickListener(mClickListener);
switch (sNumPassedTests) {
// avoid re-running passed tests, so purposely want fallthroughs here
case 0:
runTest("walk at least " + MIN_NUM_STEPS_PER_TEST
+ " steps and tap on the screen with each step",
MIN_NUM_STEPS_PER_TEST, MAX_STEP_DISCREPANCY, false, false);
case 1:
runTest("hold device still in hand", 0, MAX_STEP_DISCREPANCY, true, true);
case 2:
runTest("wave device in hand throughout test", 0, MAX_STEP_DISCREPANCY, false,
true);
default:
break;
}
}
private OnClickListener mClickListener = new OnClickListener() {
public void onClick(View v) {
if (!mCheckForMotion) {
SensorCtsHelper.beep(ToneGenerator.TONE_PROP_BEEP);
mTimestampsUserReported.add(SystemClock.elapsedRealtimeNanos());
mStepsReported = mTimestampsUserReported.size();
}
}
};
/**
* @param instructions Instruction to be shown to testers
* @param expectedSteps Number of steps expected in this test
* @param tolerance Number of steps the count can be off by and still pass
* @param vibrate If TRUE, vibration will be concurrent with the test
* @param onlyWarn If TRUE, only warn the user if the test fails. This
* option will be removed on a future release of CTS. TODO:
* remove this option
* @throws Throwable
*/
static long[] sVibratePattern = {
1000L, 500L, 1000L, 750L, 1000L, 500L, 1000L, 750L, 1000L, 1000L, 500L, 1000L,
750L, 1000L, 500L, 1000L
};
private void runTest(String instructions, int expectedSteps, int tolerance, boolean vibrate,
boolean onlyWarn)
throws Throwable {
mTimestampsUserReported.clear();
mTimestampsStepCounter.clear();
mTimestampsStepDetector.clear();
mMoveDetected = false;
mCheckForMotion = true;
appendText("Click 'Next' and " + instructions);
waitForUser();
mInitialStepCount = 0;
mStepsDetected = 0;
mStepsReported = 0;
if (vibrate) {
vibrate(sVibratePattern);
}
mCheckForMotion = (expectedSteps == 0);
startMeasurements();
long testStartTime = System.currentTimeMillis();
long testTime = 0;
while (testTime < MIN_TEST_TIME_MILLIS) {
int timeWaitSec = Math.round((MIN_TEST_TIME_MILLIS - testTime) / 1000);
clearText();
appendText("Current test: " + instructions);
appendText(String.format("%d seconds left, %d steps detected, %d reported",
timeWaitSec, mStepsDetected, mStepsReported), Color.GRAY);
Thread.sleep(1000);
testTime = System.currentTimeMillis() - testStartTime;
}
clearText();
appendText("Current test: " + instructions);
verifyMeasurements(expectedSteps, tolerance, onlyWarn);
appendText(mERNWarning + "\n" + mSCWarning, Color.YELLOW);
mCheckForMotion = false;
sNumPassedTests++;
mERNWarning = "";
mSCWarning = "";
}
private void startMeasurements() throws Throwable {
mSensorStepCounter = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
if (mSensorStepCounter != null) {
mSensorManager.registerListener(this, mSensorStepCounter,
SensorManager.SENSOR_DELAY_NORMAL);
} else {
appendText("Failed test, step counter sensor was not found", Color.RED);
Assert.fail("Step counter sensor was not found");
}
mSensorStepDetector = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
if (mSensorStepDetector != null) {
mSensorManager.registerListener(this, mSensorStepDetector,
SensorManager.SENSOR_DELAY_NORMAL);
} else {
appendText("Failed test, step detector sensor was not found", Color.RED);
Assert.fail("Step detector sensor was not found");
}
mSensorAcceleration = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mSensorAcceleration != null && mCheckForMotion) {
mSensorManager.registerListener(this, mSensorAcceleration,
SensorManager.SENSOR_DELAY_NORMAL);
}
}
private void verifyMeasurements(int stepsExpected, int tolerance, boolean onlyWarn)
throws Throwable {
if (mSensorManager != null) {
mSensorManager.unregisterListener(this);
}
Assert.assertFalse(String.format("You need to report at least %d steps", stepsExpected),
mStepsReported < stepsExpected);
double maxStepReportTime = compareTimestamps();
Assert.assertTrue(String.format("Step report time %f longer than %d seconds",
maxStepReportTime, MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS),
maxStepReportTime < MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS);
if (mCheckForMotion && !mMoveDetected) {
String message = "Movement is needed during this test";
warnOrAssert(onlyWarn, message);
}
if (Math.abs(mStepsDetected - mStepsReported) > tolerance) {
String message = String.format("Step count test: "
+ "detected %d steps but %d were expected (to within %d steps)",
mStepsDetected, mStepsReported, tolerance);
warnOrAssert(onlyWarn, message);
}
appendText("PASS step count test", Color.GREEN);
if (Math.abs(mTimestampsStepDetector.size() - mStepsReported) > tolerance) {
String message = String.format("Step detector test: "
+ "detected %d steps but %d were expected (to within %d steps)",
mTimestampsStepDetector.size(), mStepsReported, tolerance);
warnOrAssert(onlyWarn, message);
}
appendText("PASS step detection test", Color.GREEN);
logSuccess();
}
public final void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private void warnOrAssert(boolean onlyWarn, String message) throws Throwable {
if (onlyWarn) {
appendText("WARNING: " + message, Color.YELLOW);
} else {
Assert.fail("FAILED " + message);
}
}
String mERNWarning = "";
String mSCWarning = "";
public long checkTimestamp(long eventTimestamp) {
long timestamp = SystemClock.elapsedRealtimeNanos();
if (Math.abs(timestamp - eventTimestamp) > MIN_TEST_TIME_MILLIS * 1e6) {
// elapsedRealtimeNanos will lead to test failure, warn for now
mERNWarning = "WARNING: elapsedRealtimeNanos is significantly different than "
+ " sensor event timestamps. This should be rectified.";
} else {
timestamp = eventTimestamp;
}
return timestamp;
}
public void onStepCounterChanged(SensorEvent event) throws Throwable {
int steps = (int) event.values[0] - mInitialStepCount;
if (mInitialStepCount == 0) { // set the initial number of steps
mInitialStepCount = steps;
} else if (steps > 0) {
mTimestampsStepCounter.add(checkTimestamp(event.timestamp));
Assert.assertTrue(String.format("Step counter did not increase monotonically: "
+ "%d changed to %d", mStepsDetected, steps), steps >= mStepsDetected);
mStepsDetected = steps;
} else {
Assert.fail("Step Counter change called when no steps reported");
}
}
public void onStepDetectorChanged(SensorEvent event) throws Throwable {
Assert.assertEquals("Incorrect value[0] in step detector event", event.values[0], 1.0f);
mTimestampsStepDetector.add(checkTimestamp(event.timestamp));
}
public final void onSensorChanged(SensorEvent event) {
int type = event.sensor.getType();
try {
if (type == Sensor.TYPE_STEP_COUNTER) {
onStepCounterChanged(event);
} else if (type == Sensor.TYPE_STEP_DETECTOR) {
onStepDetectorChanged(event);
} else if (type == Sensor.TYPE_ACCELEROMETER) {
mMoveDetected = SensorCtsHelper.checkMovementDetection(event);
} else {
Assert.fail("Sensor type " + type + " called when not registered for by this test");
}
} catch (Throwable ae) {
mSCWarning = ae.getMessage();
}
}
protected double compareTimestamps() {
double timeDeltaInSec;
double maxTimeDeltaInSec = 0;
StringBuilder reportLine = new StringBuilder();
reportLine.append("Reported Step: Step Detector / Counter Latency (sec)\n");
for (int eventCounter = 0; eventCounter < mStepsReported; eventCounter++) {
reportLine.append((eventCounter + 1) + ": ");
if (eventCounter < mTimestampsStepDetector.size()) {
timeDeltaInSec = (mTimestampsStepDetector.get(eventCounter)
- mTimestampsUserReported.get(eventCounter)) / NANOSECONDS_IN_SEC;
maxTimeDeltaInSec = Math.max(maxTimeDeltaInSec, Math.abs(timeDeltaInSec));
reportLine.append(String.format("%.2f", timeDeltaInSec));
} else {
reportLine.append("--");
}
reportLine.append(" / ");
if (eventCounter < mTimestampsStepCounter.size()) {
timeDeltaInSec = (mTimestampsStepCounter.get(eventCounter)
- mTimestampsUserReported.get(eventCounter)) / NANOSECONDS_IN_SEC;
maxTimeDeltaInSec = Math.max(maxTimeDeltaInSec, Math.abs(timeDeltaInSec));
reportLine.append(String.format("%.2f", timeDeltaInSec));
} else {
reportLine.append("--");
}
reportLine.append("\n");
}
appendText(reportLine.toString(), Color.GRAY);
return maxTimeDeltaInSec;
}
protected void vibrate(long[] pattern) {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if(v==null) {
appendText("Cannot access vibrator for this test...continuing anyway", Color.YELLOW);
} else {
v.vibrate(pattern, -1);
}
}
@Override
protected void onResume() {
super.onResume();
if (mSensorManager == null) {
mSensorManager = (SensorManager) getApplicationContext()
.getSystemService(Context.SENSOR_SERVICE);
}
if (mSensorStepCounter != null) {
mSensorManager.registerListener(this, mSensorStepCounter,
SensorManager.SENSOR_DELAY_NORMAL);
}
if (mSensorStepDetector != null) {
mSensorManager.registerListener(this, mSensorStepDetector,
SensorManager.SENSOR_DELAY_NORMAL);
}
if (mSensorAcceleration != null && mCheckForMotion) {
mSensorManager.registerListener(this, mSensorAcceleration,
SensorManager.SENSOR_DELAY_NORMAL);
}
}
@Override
protected void onPause() {
super.onPause();
if (mSensorManager != null) {
mSensorManager.unregisterListener(this);
}
}
}