| /* |
| * 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 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.ContinuousEventSanitizedVerification; |
| import android.hardware.cts.helpers.sensorverification.EventGapVerification; |
| import android.hardware.cts.helpers.sensorverification.EventOrderingVerification; |
| import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification; |
| import android.os.Build.VERSION_CODES; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.PowerManager; |
| import android.os.SystemClock; |
| import android.platform.test.annotations.AppModeFull; |
| import android.platform.test.annotations.Presubmit; |
| import android.util.Log; |
| |
| import com.android.compatibility.common.util.CddTest; |
| import com.android.compatibility.common.util.PropertyUtil; |
| |
| import com.google.common.collect.ArrayListMultimap; |
| import com.google.common.collect.Multimap; |
| |
| import junit.framework.Assert; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| 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; |
| private List<Sensor> mAndroidSensorList; |
| |
| @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."); |
| } |
| |
| mAndroidSensorList = new ArrayList<>(); |
| for (Sensor s : mSensorList) { |
| if (s.getType() < Sensor.TYPE_DEVICE_PRIVATE_BASE && |
| (!context.getPackageManager().isInstantApp() || s.getType() != Sensor.TYPE_HEART_RATE)) { |
| mAndroidSensorList.add(s); |
| } |
| } |
| |
| 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(); |
| } |
| } |
| |
| /** |
| * testSensorOperations |
| * |
| * Because we can't know every sensors unit details, so we can't assert |
| * get values with specified values. |
| */ |
| @CddTest(requirement = "7.3.1/C-2-1") |
| public void testSensorOperations_accelerometer() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); |
| boolean hasAccelerometer = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_ACCELEROMETER); |
| // accelerometer sensor is optional |
| if (hasAccelerometer) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_ACCELEROMETER, sensor.getType()); |
| assertSensorValues(sensor); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| public void testSensorOperations_stepCounter() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); |
| boolean hasStepCounter = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_STEP_COUNTER); |
| // stepcounter sensor is optional |
| if (hasStepCounter) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_STEP_COUNTER, sensor.getType()); |
| assertSensorValues(sensor); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| public void testSensorOperations_stepDetector() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR); |
| boolean hasStepDetector = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_STEP_DETECTOR); |
| // stepdetector sensor is optional |
| if (hasStepDetector) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_STEP_DETECTOR, sensor.getType()); |
| assertSensorValues(sensor); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| public void testSensorOperations_magneticField() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); |
| boolean hasCompass = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_COMPASS); |
| // compass sensor is optional |
| if (hasCompass) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_MAGNETIC_FIELD, sensor.getType()); |
| assertSensorValues(sensor); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| @CddTest(requirement = "7.3.4/C-2-1") |
| public void testSensorOperations_gyroscope() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); |
| boolean hasGyroscope = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_GYROSCOPE); |
| // gyroscope sensor is optional |
| if (hasGyroscope) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_GYROSCOPE, sensor.getType()); |
| assertSensorValues(sensor); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| public void testSensorOperations_pressure() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); |
| boolean hasPressure = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_BAROMETER); |
| // pressure sensor is optional |
| if (hasPressure) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_PRESSURE, sensor.getType()); |
| assertSensorValues(sensor); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| @SuppressWarnings("deprecation") |
| public void testSensorOperations_orientation() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); |
| // Note: orientation sensor is deprecated. |
| if (sensor != null) { |
| assertEquals(Sensor.TYPE_ORIENTATION, sensor.getType()); |
| assertSensorValues(sensor); |
| } |
| } |
| |
| @SuppressWarnings("deprecation") |
| public void testSensorOperations_temperature() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE); |
| // temperature sensor is optional |
| if (sensor != null) { |
| assertEquals(Sensor.TYPE_TEMPERATURE, sensor.getType()); |
| assertSensorValues(sensor); |
| } |
| } |
| |
| public void testSensorOperations_hingeAngle() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE); |
| boolean hasHingeAngle = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_HINGE_ANGLE); |
| |
| if (hasHingeAngle) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_HINGE_ANGLE, sensor.getType()); |
| assertSensorValues(sensor); |
| assertTrue("Max range must not be larger than 360. Range=" + sensor.getMaximumRange() |
| + " " + sensor.getName(), sensor.getMaximumRange() <= 360); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| @CddTest(requirement = "7.3.1/C-3-1") |
| public void testSensorOperations_accelerometerLimitedAxes() { |
| validateLimitedAxesImuSensorType(Sensor.TYPE_ACCELEROMETER_LIMITED_AXES, |
| PackageManager.FEATURE_SENSOR_ACCELEROMETER_LIMITED_AXES); |
| } |
| |
| public void testSensorOperations_gyroscopeLimitedAxes() { |
| validateLimitedAxesImuSensorType(Sensor.TYPE_GYROSCOPE_LIMITED_AXES, |
| PackageManager.FEATURE_SENSOR_GYROSCOPE_LIMITED_AXES); |
| } |
| |
| @CddTest(requirement = "7.3.4/C-3-1") |
| public void testSensorOperations_accelerometerLimitedAxesUncalibrated() { |
| validateLimitedAxesImuSensorType(Sensor.TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED, |
| PackageManager.FEATURE_SENSOR_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED); |
| } |
| |
| public void testSensorOperations_gyroscopeLimitedAxesUncalibrated() { |
| validateLimitedAxesImuSensorType(Sensor.TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED, |
| PackageManager.FEATURE_SENSOR_GYROSCOPE_LIMITED_AXES_UNCALIBRATED); |
| } |
| |
| private void validateLimitedAxesImuSensorType(int sensorType, String systemFeature) { |
| Sensor sensor = mSensorManager.getDefaultSensor(sensorType); |
| boolean hasSensorFeature = getContext().getPackageManager().hasSystemFeature(systemFeature); |
| if (hasSensorFeature) { |
| assertNotNull(sensor); |
| assertEquals(sensorType, sensor.getType()); |
| assertSensorValues(sensor); |
| } else { |
| assertNull(sensor); |
| } |
| } |
| |
| public void testSensorOperations_heading() { |
| Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEADING); |
| boolean hasHeadingSensor = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_SENSOR_HEADING); |
| boolean isAutomotive = mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_AUTOMOTIVE); |
| if (isAutomotive && hasHeadingSensor) { |
| assertNotNull(sensor); |
| assertEquals(Sensor.TYPE_HEADING, sensor.getType()); |
| assertSensorValues(sensor); |
| assertTrue("Max range must not be greater or equal to 360. Range=" |
| + sensor.getMaximumRange() + " " + sensor.getName(), |
| sensor.getMaximumRange() < 360); |
| } else if (isAutomotive) { |
| assertNull(sensor); |
| } else { |
| // There isn't good test coverage for heading, particularly for non-automotive devices. |
| // So if a non-automotive device wants to implement this, requirements for the sensor |
| // and how to test for those requirements should be re-discussed. |
| assertNull("If the heading sensor is being implemented on a non-automotive device, " |
| + "the team would love to hear from you. Please reach out!", sensor); |
| } |
| } |
| |
| @AppModeFull(reason = "Instant apps cannot access body sensors") |
| public void testBodySensorOperations() { |
| 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); |
| } |
| } |
| |
| private void assertAllSensorsNameUniqueness() { |
| Multimap<Integer, String> sensorTypeNameMap = ArrayListMultimap.create(); |
| |
| for (Sensor sensor : mSensorList) { |
| assertFalse("Duplicate sensor name " + sensor.getName() + " for type " + sensor.getType(), |
| sensorTypeNameMap.containsEntry(sensor.getType(), sensor.getName())); |
| sensorTypeNameMap.put(sensor.getType(), sensor.getName()); |
| } |
| } |
| |
| public void testValuesForAllSensors() { |
| for (Sensor sensor : mSensorList) { |
| assertSensorValues(sensor); |
| } |
| assertAllSensorsNameUniqueness(); |
| } |
| |
| 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()); |
| } |
| |
| private void hasDefaultWakeupSensorOrEmpty(int sensorType, String sensorName) { |
| Sensor sensor = mSensorManager.getDefaultSensor(sensorType); |
| if (sensor == null) return; |
| |
| assertTrue("Default " + sensorName + " sensor is not a wake-up sensor", sensor.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. |
| @Presubmit |
| 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)); |
| |
| hasDefaultWakeupSensorOrEmpty(Sensor.TYPE_PROXIMITY, "proximity"); |
| hasDefaultWakeupSensorOrEmpty(Sensor.TYPE_HINGE_ANGLE, "hinge"); |
| } |
| |
| 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()); |
| } |
| } |
| } |
| |
| @Presubmit |
| 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); |
| } |
| |
| /** |
| * Verifies that if the UID is idle the continuous events are being reported |
| * but sanitized - all events are the same as the first one delivered except |
| * for their timestamps. From the point of view of an idle app these events are |
| * being properly generated but the sensor reading does not change - privacy. |
| */ |
| // TODO: remove when parametrized tests are supported and EventTimestampSynchronization |
| public void testSanitizedContinuousEventsUidIdle() throws Exception { |
| ArrayList<Throwable> errorsFound = new ArrayList<>(); |
| for (Sensor sensor : mAndroidSensorList) { |
| // If the UID is active no sanitization should be performed |
| verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, |
| 5 /* duration */, TimeUnit.SECONDS, "continuous event", |
| false /* sanitized */, errorsFound); |
| verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), |
| 5 /* duration */, TimeUnit.SECONDS, "continuous event", |
| false /* sanitized */, errorsFound); |
| |
| // If the UID is idle sanitization should be performed |
| |
| SensorCtsHelper.makeMyPackageIdle(); |
| try { |
| verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, |
| 5 /* duration */, TimeUnit.SECONDS, "continuous event", |
| true /* sanitized */, errorsFound); |
| verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), |
| 5 /* duration */, TimeUnit.SECONDS, "continuous event", |
| true /* sanitized */, errorsFound); |
| } finally { |
| SensorCtsHelper.makeMyPackageActive(); |
| } |
| |
| // If the UID is active no sanitization should be performed |
| verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, |
| 5 /* duration */, TimeUnit.SECONDS, "continuous event", |
| false /* sanitized */, errorsFound); |
| verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), |
| 5 /* duration */, TimeUnit.SECONDS, "continuous event", |
| false /* sanitized */, errorsFound); |
| } |
| assertOnErrors(errorsFound); |
| } |
| |
| // TODO: remove when parametrized tests are supported and EventTimestampSynchronization |
| // verification is added to default verifications |
| public void testSensorTimeStamps() throws Exception { |
| ArrayList<Throwable> errorsFound = new ArrayList<>(); |
| for (Sensor sensor : mAndroidSensorList) { |
| // test both continuous and batching mode sensors |
| verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, |
| 20 /* duration */, TimeUnit.SECONDS, "timestamp", false |
| /* sanitized */, errorsFound); |
| verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), |
| 20 /* duration */, TimeUnit.SECONDS, "timestamp", |
| false /* sanitized */, errorsFound); |
| } |
| assertOnErrors(errorsFound); |
| } |
| |
| // TODO: remove when parameterized tests are supported (see SensorBatchingTests.java) |
| public void testBatchAndFlush() throws Exception { |
| SensorCtsHelper.sleep(3, TimeUnit.SECONDS); |
| ArrayList<Throwable> errorsFound = new ArrayList<>(); |
| for (Sensor sensor : mAndroidSensorList) { |
| verifyRegisterListenerCallFlush(sensor, null /* handler */, errorsFound, |
| false /* flushWhileIdle */); |
| } |
| assertOnErrors(errorsFound); |
| } |
| |
| /** |
| * Verifies that if the UID is idle flush events are reported. Since |
| * these events have no payload with private data they are working as |
| * for a non-idle UID. |
| */ |
| // TODO: remove when parametized tests are supported and EventTimestampSynchronization |
| public void testBatchAndFlushUidIdle() throws Exception { |
| SensorCtsHelper.sleep(3, TimeUnit.SECONDS); |
| ArrayList<Throwable> errorsFound = new ArrayList<>(); |
| for (Sensor sensor : mAndroidSensorList) { |
| verifyRegisterListenerCallFlush(sensor, null /* handler */, errorsFound, |
| true /* flushWhileIdle */); |
| } |
| assertOnErrors(errorsFound); |
| } |
| |
| /** |
| * Verifies that sensor events arrive in the given message queue (Handler). |
| */ |
| public void testBatchAndFlushWithHandler() throws Exception { |
| SensorCtsHelper.sleep(3, TimeUnit.SECONDS); |
| Sensor sensor = null; |
| for (Sensor s : mAndroidSensorList) { |
| 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). |
| */ |
| public void testBatchAndFlushUseDefaultHandler() throws Exception { |
| SensorCtsHelper.sleep(3, TimeUnit.SECONDS); |
| Sensor sensor = null; |
| for (Sensor s : mAndroidSensorList) { |
| 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 : mAndroidSensorList) { |
| 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 */, |
| false /* flushWhileIdle */); |
| 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); |
| |
| // Only assert sensor resolution is non-zero for official sensor types since that's what's |
| // required by the CDD. |
| if (sensor.getType() < MAX_OFFICIAL_ANDROID_SENSOR_TYPE) { |
| assertTrue("Max resolution must be non-zero and positive. Resolution=" + sensor.getResolution() + |
| " " + sensor.getName(), sensor.getResolution() > 0); |
| } else { |
| assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() + |
| " " + sensor.getName(), sensor.getResolution() >= 0); |
| } |
| |
| boolean hasHifiSensors = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_HIFI_SENSORS); |
| if (SensorCtsHelper.hasMaxResolutionRequirement(sensor, hasHifiSensors)) { |
| float maxResolution = SensorCtsHelper.getRequiredMaxResolutionForSensor(sensor); |
| assertTrue("Resolution must be <= " + maxResolution + ". Resolution=" + |
| sensor.getResolution() + " " + sensor.getName(), |
| sensor.getResolution() <= maxResolution); |
| } |
| |
| // The minimum resolution requirement was introduced to the CDD in R so |
| // it's only possible to assert compliance for devices that release with |
| // R or later. |
| if (PropertyUtil.getFirstApiLevel() >= VERSION_CODES.R && |
| SensorCtsHelper.hasMinResolutionRequirement(sensor)) { |
| float minResolution = SensorCtsHelper.getRequiredMinResolutionForSensor(sensor); |
| assertTrue("Resolution must be >= " + minResolution + ". Resolution =" + |
| sensor.getResolution() + " " + sensor.getName(), |
| sensor.getResolution() >= minResolution); |
| } |
| |
| 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()} and that the events are sanitized/non-sanitized. |
| */ |
| private void verifyLongActivation( |
| Sensor sensor, |
| int maxReportLatencyUs, |
| long duration, |
| TimeUnit durationTimeUnit, |
| String testType, |
| boolean sanitized, |
| 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, duration, durationTimeUnit); |
| if (sanitized) { |
| final long verificationDelayNano = TimeUnit.NANOSECONDS.convert( |
| maxReportLatencyUs, TimeUnit.MICROSECONDS) * 2; |
| operation.addVerification(ContinuousEventSanitizedVerification |
| .getDefault(environment, verificationDelayNano)); |
| } else { |
| operation.addVerification(EventGapVerification.getDefault(environment)); |
| operation.addVerification(EventOrderingVerification.getDefault(environment)); |
| operation.addVerification(EventTimestampSynchronizationVerification |
| .getDefault(environment)); |
| } |
| Log.i(TAG, "Running " + testType + " 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, |
| boolean flushWhileIdle) |
| 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 */, |
| flushWhileIdle); |
| 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; |
| private final boolean mFlushWhileIdle; |
| |
| public FlushExecutor(TestSensorEnvironment environment, int eventCount, |
| boolean flushWhileIdle) { |
| mEnvironment = environment; |
| mEventCount = eventCount; |
| mFlushWhileIdle = flushWhileIdle; |
| } |
| |
| /** |
| * 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 Exception { |
| int sensorReportingMode = mEnvironment.getSensor().getReportingMode(); |
| try { |
| CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount); |
| if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) { |
| listener.waitForEvents(eventLatch, mEventCount, true); |
| } |
| if (mFlushWhileIdle) { |
| SensorCtsHelper.makeMyPackageIdle(); |
| sensorManager.assertFlushFail(); |
| } else { |
| CountDownLatch flushLatch = sensorManager.requestFlush(); |
| listener.waitForFlushComplete(flushLatch, true); |
| } |
| } finally { |
| sensorManager.unregisterListener(); |
| if (mFlushWhileIdle) { |
| SensorCtsHelper.makeMyPackageActive(); |
| } |
| } |
| } |
| } |
| |
| 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) {} |
| } |
| |
| } |