blob: 2bbf053936246d27518eb5076d3ec371085c674a [file] [log] [blame]
/*
* Copyright (C) 2008 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 android.hardware.cts;
import com.android.cts.util.TimeoutReq;
import junit.framework.Assert;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorEventListener2;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.cts.helpers.SensorCtsHelper;
import android.hardware.cts.helpers.SensorNotSupportedException;
import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEventListener;
import android.hardware.cts.helpers.TestSensorManager;
import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
import android.hardware.cts.helpers.sensorverification.EventGapVerification;
import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class SensorTest extends SensorTestCase {
private static final String TAG = "SensorTest";
// Test only SDK defined sensors. Any sensors with type > 100 are ignored.
private static final int MAX_OFFICIAL_ANDROID_SENSOR_TYPE = 100;
private PowerManager.WakeLock mWakeLock;
private SensorManager mSensorManager;
private TestSensorManager mTestSensorManager;
private NullTriggerEventListener mNullTriggerEventListener;
private NullSensorEventListener mNullSensorEventListener;
private Sensor mTriggerSensor;
private List<Sensor> mSensorList;
@Override
protected void setUp() throws Exception {
Context context = getContext();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mNullTriggerEventListener = new NullTriggerEventListener();
mNullSensorEventListener = new NullSensorEventListener();
mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
assertNotNull("SensorList was null.", mSensorList);
if (mSensorList.isEmpty()) {
// several devices will not have sensors, so we need to skip the tests in those cases
throw new SensorTestStateNotSupportedException(
"Sensors are not available in the system.");
}
mWakeLock.acquire();
}
@Override
protected void tearDown() {
if (mSensorManager != null) {
// SensorManager will check listener and status, so just unregister listener
mSensorManager.unregisterListener(mNullSensorEventListener);
if (mTriggerSensor != null) {
mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
mTriggerSensor = null;
}
}
if (mTestSensorManager != null) {
mTestSensorManager.unregisterListener();
mTestSensorManager = null;
}
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
}
}
@SuppressWarnings("deprecation")
public void testSensorOperations() {
// Because we can't know every sensors unit details, so we can't assert
// get values with specified values.
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
boolean hasAccelerometer = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_ACCELEROMETER);
// accelerometer sensor is optional
if (hasAccelerometer) {
assertEquals(Sensor.TYPE_ACCELEROMETER, sensor.getType());
assertSensorValues(sensor);
} else {
assertNull(sensor);
}
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
boolean hasStepCounter = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_STEP_COUNTER);
// stepcounter sensor is optional
if (hasStepCounter) {
assertEquals(Sensor.TYPE_STEP_COUNTER, sensor.getType());
assertSensorValues(sensor);
} else {
assertNull(sensor);
}
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
boolean hasStepDetector = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
// stepdetector sensor is optional
if (hasStepDetector) {
assertEquals(Sensor.TYPE_STEP_DETECTOR, sensor.getType());
assertSensorValues(sensor);
} else {
assertNull(sensor);
}
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
boolean hasHeartRate = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_HEART_RATE);
// heartrate sensor is optional
if (hasHeartRate) {
assertEquals(Sensor.TYPE_HEART_RATE, sensor.getType());
assertSensorValues(sensor);
} else {
assertNull(sensor);
}
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
boolean hasCompass = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_COMPASS);
// compass sensor is optional
if (hasCompass) {
assertEquals(Sensor.TYPE_MAGNETIC_FIELD, sensor.getType());
assertSensorValues(sensor);
} else {
assertNull(sensor);
}
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
// Note: orientation sensor is deprecated.
if (sensor != null) {
assertEquals(Sensor.TYPE_ORIENTATION, sensor.getType());
assertSensorValues(sensor);
}
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE);
// temperature sensor is optional
if (sensor != null) {
assertEquals(Sensor.TYPE_TEMPERATURE, sensor.getType());
assertSensorValues(sensor);
}
}
public void testValuesForAllSensors() {
for (Sensor sensor : mSensorList) {
assertSensorValues(sensor);
}
}
private void hasOnlyOneWakeUpSensorOrEmpty(List<Sensor> sensors) {
if (sensors == null || sensors.isEmpty()) return;
if (sensors.size() > 1) {
fail("More than one " + sensors.get(0).getName() + " defined.");
return;
}
assertTrue(sensors.get(0).getName() + " defined as non-wake-up sensor",
sensors.get(0).isWakeUpSensor());
}
// Some sensors like proximity, significant motion etc. are defined as wake-up sensors by
// default. Check if the wake-up flag is set correctly.
public void testWakeUpFlags() {
final int TYPE_WAKE_GESTURE = 23;
final int TYPE_GLANCE_GESTURE = 24;
final int TYPE_PICK_UP_GESTURE = 25;
hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(Sensor.TYPE_SIGNIFICANT_MOTION));
hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(TYPE_WAKE_GESTURE));
hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(TYPE_GLANCE_GESTURE));
hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(TYPE_PICK_UP_GESTURE));
List<Sensor> proximity_sensors = mSensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
if (proximity_sensors.isEmpty()) return;
boolean hasWakeUpProximitySensor = false;
for (Sensor sensor : proximity_sensors) {
if (sensor.isWakeUpSensor()) {
hasWakeUpProximitySensor = true;
break;
}
}
assertTrue("No wake-up proximity sensors implemented", hasWakeUpProximitySensor);
}
public void testGetDefaultSensorWithWakeUpFlag() {
// With wake-up flags set to false, the sensor returned should be a non wake-up sensor.
for (Sensor sensor : mSensorList) {
Sensor curr_sensor = mSensorManager.getDefaultSensor(sensor.getType(), false);
if (curr_sensor != null) {
assertFalse("getDefaultSensor wakeup=false returns a wake-up sensor" +
curr_sensor.getName(),
curr_sensor.isWakeUpSensor());
}
curr_sensor = mSensorManager.getDefaultSensor(sensor.getType(), true);
if (curr_sensor != null) {
assertTrue("getDefaultSensor wake-up returns non wake sensor" +
curr_sensor.getName(),
curr_sensor.isWakeUpSensor());
}
}
}
public void testSensorStringTypes() {
for (Sensor sensor : mSensorList) {
if (sensor.getType() < MAX_OFFICIAL_ANDROID_SENSOR_TYPE &&
!sensor.getStringType().startsWith("android.sensor.")) {
fail("StringType not set correctly for android defined sensor " +
sensor.getName() + " " + sensor.getStringType());
}
}
}
public void testRequestTriggerWithNonTriggerSensor() {
mTriggerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mTriggerSensor == null) {
throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
boolean result =
mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
assertFalse(result);
}
public void testCancelTriggerWithNonTriggerSensor() {
mTriggerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mTriggerSensor == null) {
throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
boolean result =
mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
assertFalse(result);
}
public void testRegisterWithTriggerSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
if (sensor == null) {
throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
}
boolean result = mSensorManager.registerListener(
mNullSensorEventListener,
sensor,
SensorManager.SENSOR_DELAY_NORMAL);
assertFalse(result);
}
public void testRegisterTwiceWithSameSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (sensor == null) {
throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
boolean result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
SensorManager.SENSOR_DELAY_NORMAL);
assertTrue(result);
result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
SensorManager.SENSOR_DELAY_NORMAL);
assertFalse(result);
}
// TODO: remove when parametized tests are supported and EventTimestampSynchronization
// verification is added to default verifications
@TimeoutReq(minutes=60)
public void testSensorTimeStamps() throws Exception {
ArrayList<Throwable> errorsFound = new ArrayList<>();
for (Sensor sensor : mSensorList) {
// test both continuous and batching mode sensors
verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, errorsFound);
verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), errorsFound);
}
assertOnErrors(errorsFound);
}
// TODO: remove when parameterized tests are supported (see SensorBatchingTests.java)
@TimeoutReq(minutes=20)
public void testBatchAndFlush() throws Exception {
SensorCtsHelper.sleep(3, TimeUnit.SECONDS);
ArrayList<Throwable> errorsFound = new ArrayList<>();
for (Sensor sensor : mSensorList) {
verifyRegisterListenerCallFlush(sensor, null /* handler */, errorsFound);
}
assertOnErrors(errorsFound);
}
/**
* Verifies that sensor events arrive in the given message queue (Handler).
*/
@TimeoutReq(minutes=10)
public void testBatchAndFlushWithHandler() throws Exception {
SensorCtsHelper.sleep(3, TimeUnit.SECONDS);
Sensor sensor = null;
for (Sensor s : mSensorList) {
if (s.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
sensor = s;
break;
}
}
if (sensor == null) {
throw new SensorTestStateNotSupportedException(
"There are no Continuous sensors in the device.");
}
TestSensorEnvironment environment = new TestSensorEnvironment(
getContext(),
sensor,
SensorManager.SENSOR_DELAY_FASTEST,
(int) TimeUnit.SECONDS.toMicros(5));
mTestSensorManager = new TestSensorManager(environment);
HandlerThread handlerThread = new HandlerThread("sensorThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
TestSensorEventListener listener = new TestSensorEventListener(environment, handler);
CountDownLatch eventLatch = mTestSensorManager.registerListener(listener, 1);
listener.waitForEvents(eventLatch, 1, true);
CountDownLatch flushLatch = mTestSensorManager.requestFlush();
listener.waitForFlushComplete(flushLatch, true);
listener.assertEventsReceivedInHandler();
}
/**
* Explicit testing the SensorManager.registerListener(SensorEventListener, Sensor, int, int).
*/
@TimeoutReq(minutes=10)
public void testBatchAndFlushUseDefaultHandler() throws Exception {
SensorCtsHelper.sleep(3, TimeUnit.SECONDS);
Sensor sensor = null;
for (Sensor s : mSensorList) {
if (s.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
sensor = s;
break;
}
}
if (sensor == null) {
throw new SensorTestStateNotSupportedException(
"There are no Continuous sensors in the device.");
}
TestSensorEnvironment environment = new TestSensorEnvironment(
getContext(),
sensor,
SensorManager.SENSOR_DELAY_FASTEST,
(int) TimeUnit.SECONDS.toMicros(5));
mTestSensorManager = new TestSensorManager(environment);
TestSensorEventListener listener = new TestSensorEventListener(environment, null);
// specifyHandler <= false, use the SensorManager API without Handler parameter
CountDownLatch eventLatch = mTestSensorManager.registerListener(listener, 1, false);
listener.waitForEvents(eventLatch, 1, true);
CountDownLatch flushLatch = mTestSensorManager.requestFlush();
listener.waitForFlushComplete(flushLatch, true);
listener.assertEventsReceivedInHandler();
}
// TODO: after L release move to SensorBatchingTests and run in all sensors with default
// verifications enabled
public void testBatchAndFlushWithMultipleSensors() throws Exception {
SensorCtsHelper.sleep(3, TimeUnit.SECONDS);
final int maxSensors = 3;
final int maxReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(10);
List<Sensor> sensorsToTest = new ArrayList<Sensor>();
for (Sensor sensor : mSensorList) {
if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
sensorsToTest.add(sensor);
if (sensorsToTest.size() == maxSensors) break;
}
}
final int numSensorsToTest = sensorsToTest.size();
if (numSensorsToTest == 0) {
return;
}
StringBuilder builder = new StringBuilder();
ParallelSensorOperation parallelSensorOperation = new ParallelSensorOperation();
for (Sensor sensor : sensorsToTest) {
TestSensorEnvironment environment = new TestSensorEnvironment(
getContext(),
sensor,
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST,
maxReportLatencyUs);
FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
parallelSensorOperation.add(new TestSensorOperation(environment, executor));
builder.append(sensor.getName()).append(", ");
}
Log.i(TAG, "Testing batch/flush for sensors: " + builder);
parallelSensorOperation.execute(getCurrentTestNode());
}
private void assertSensorValues(Sensor sensor) {
assertTrue("Max range must be positive. Range=" + sensor.getMaximumRange()
+ " " + sensor.getName(), sensor.getMaximumRange() >= 0);
assertTrue("Max power must be positive. Power=" + sensor.getPower() + " " +
sensor.getName(), sensor.getPower() >= 0);
assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
" " + sensor.getName(), sensor.getResolution() >= 0);
assertNotNull("Vendor name must not be null " + sensor.getName(), sensor.getVendor());
assertTrue("Version must be positive version=" + sensor.getVersion() + " " +
sensor.getName(), sensor.getVersion() > 0);
int fifoMaxEventCount = sensor.getFifoMaxEventCount();
int fifoReservedEventCount = sensor.getFifoReservedEventCount();
assertTrue(fifoMaxEventCount >= 0);
assertTrue(fifoReservedEventCount >= 0);
assertTrue(fifoReservedEventCount <= fifoMaxEventCount);
if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
assertTrue("One shot sensors should have zero FIFO Size " + sensor.getName(),
sensor.getFifoMaxEventCount() == 0);
assertTrue("One shot sensors should have zero FIFO Size " + sensor.getName(),
sensor.getFifoReservedEventCount() == 0);
}
}
@SuppressWarnings("deprecation")
public void testLegacySensorOperations() {
final SensorManager mSensorManager =
(SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
// We expect the set of sensors reported by the new and legacy APIs to be consistent.
int sensors = 0;
if (mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
sensors |= SensorManager.SENSOR_ACCELEROMETER;
}
if (mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null) {
sensors |= SensorManager.SENSOR_MAGNETIC_FIELD;
}
if (mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION) != null) {
sensors |= SensorManager.SENSOR_ORIENTATION | SensorManager.SENSOR_ORIENTATION_RAW;
}
assertEquals(sensors, mSensorManager.getSensors());
}
/**
* Verifies that a continuous sensor produces events that have timestamps synchronized with
* {@link SystemClock#elapsedRealtimeNanos()}.
*/
private void verifyLongActivation(
Sensor sensor,
int maxReportLatencyUs,
ArrayList<Throwable> errorsFound) throws InterruptedException {
if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
return;
}
try {
TestSensorEnvironment environment = new TestSensorEnvironment(
getContext(),
sensor,
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST,
maxReportLatencyUs);
TestSensorOperation operation =
TestSensorOperation.createOperation(environment, 20, TimeUnit.SECONDS);
operation.addVerification(EventGapVerification.getDefault(environment));
operation.addVerification(EventOrderingVerification.getDefault(environment));
operation.addVerification(
EventTimestampSynchronizationVerification.getDefault(environment));
Log.i(TAG, "Running timestamp test on: " + sensor.getName());
operation.execute(getCurrentTestNode());
} catch (InterruptedException e) {
// propagate so the test can stop
throw e;
} catch (Throwable e) {
errorsFound.add(e);
Log.e(TAG, e.getMessage());
}
}
/**
* Verifies that a client can listen for events, and that
* {@link SensorManager#flush(SensorEventListener)} will trigger the appropriate notification
* for {@link SensorEventListener2#onFlushCompleted(Sensor)}.
*/
private void verifyRegisterListenerCallFlush(
Sensor sensor,
Handler handler,
ArrayList<Throwable> errorsFound)
throws InterruptedException {
if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
return;
}
try {
TestSensorEnvironment environment = new TestSensorEnvironment(
getContext(),
sensor,
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST,
(int) TimeUnit.SECONDS.toMicros(10));
FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
TestSensorOperation operation = new TestSensorOperation(environment, executor, handler);
Log.i(TAG, "Running flush test on: " + sensor.getName());
operation.execute(getCurrentTestNode());
} catch (InterruptedException e) {
// propagate so the test can stop
throw e;
} catch (Throwable e) {
errorsFound.add(e);
Log.e(TAG, e.getMessage());
}
}
private void assertOnErrors(List<Throwable> errorsFound) {
if (!errorsFound.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (Throwable error : errorsFound) {
builder.append(error.getMessage()).append("\n");
}
Assert.fail(builder.toString());
}
}
/**
* A delegate that drives the execution of Batch/Flush tests.
* It performs several operations in order:
* - registration
* - for continuous sensors it first ensures that the FIFO is filled
* - if events do not arrive on time, an assert will be triggered
* - requests flush of sensor data
* - waits for {@link SensorEventListener2#onFlushCompleted(Sensor)}
* - if the event does not arrive, an assert will be triggered
*/
private class FlushExecutor implements TestSensorOperation.Executor {
private final TestSensorEnvironment mEnvironment;
private final int mEventCount;
public FlushExecutor(TestSensorEnvironment environment, int eventCount) {
mEnvironment = environment;
mEventCount = eventCount;
}
/**
* Consider only continuous mode sensors for testing register listener.
*
* For on-change sensors, we only use
* {@link TestSensorManager#registerListener(TestSensorEventListener)} to associate the
* listener with the sensor. So that {@link TestSensorManager#requestFlush()} can be
* invoked on it.
*/
@Override
public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
throws InterruptedException {
int sensorReportingMode = mEnvironment.getSensor().getReportingMode();
try {
CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount);
if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
listener.waitForEvents(eventLatch, mEventCount, true);
}
CountDownLatch flushLatch = sensorManager.requestFlush();
listener.waitForFlushComplete(flushLatch, true);
} finally {
sensorManager.unregisterListener();
}
}
}
private class NullTriggerEventListener extends TriggerEventListener {
@Override
public void onTrigger(TriggerEvent event) {}
}
private class NullSensorEventListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent event) {}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
}
}