Batching support: Check & verify FIFO length
Change-Id: I50f933f9f89120a61af9f8ffdd62c2e733467aa5
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingFifoTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingFifoTest.java
new file mode 100644
index 0000000..4c6362a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingFifoTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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.SensorManager;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.FifoLengthVerification;
+
+/**
+ * Checks the minimum Hardware FIFO length for each of the Hardware sensor.
+ * Further verifies if the advertised FIFO (Sensor.getFifoMaxEventCount()) is actually allocated
+ * for the sensor.
+ *
+ */
+public class SensorBatchingFifoTest extends SensorTestCase {
+ private static final int ACCELEROMETER_MIN_FIFO_LENGTH = 3000;
+ private static final int UNCAL_MAGNETOMETER_MIN_FIFO_LENGTH = 600;
+ private static final int PRESSURE_MIN_FIFO_LENGTH = 300;
+ private static final int GAME_ROTATION_VECTOR_MIN_FIFO_LENGTH = 300;
+ private static final int PROXIMITY_SENSOR_MIN_FIFO_LENGTH = 300;
+ private static final int STEP_DETECTOR_MIN_FIFO_LENGTH = 100;
+
+ private static final int SAMPLING_INTERVAL = 1000; /* every 1ms */
+ private static final String TAG = "batching_fifo_test";
+
+ private SensorManager mSensorManager;
+ private boolean mHasHifiSensors;
+ @Override
+ protected void setUp() throws Exception {
+ mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
+ mHasHifiSensors = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_HIFI_SENSORS);
+ }
+
+ public void testAccelerometerFifoLength() throws Throwable {
+ if (!mHasHifiSensors) return;
+ runBatchingSensorFifoTest(
+ Sensor.TYPE_ACCELEROMETER,
+ checkMinFifoLength(Sensor.TYPE_ACCELEROMETER, ACCELEROMETER_MIN_FIFO_LENGTH));
+ }
+
+ public void testUncalMagnetometerFifoLength() throws Throwable {
+ if (!mHasHifiSensors) return;
+ runBatchingSensorFifoTest(
+ Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+ checkMinFifoLength(
+ Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+ UNCAL_MAGNETOMETER_MIN_FIFO_LENGTH));
+ }
+
+ public void testPressureFifoLength() throws Throwable {
+ if (!mHasHifiSensors) return;
+ runBatchingSensorFifoTest(
+ Sensor.TYPE_PRESSURE,
+ checkMinFifoLength(Sensor.TYPE_PRESSURE, PRESSURE_MIN_FIFO_LENGTH));
+ }
+
+ public void testGameRotationVectorFifoLength() throws Throwable {
+ if (!mHasHifiSensors) return;
+ runBatchingSensorFifoTest(
+ Sensor.TYPE_GAME_ROTATION_VECTOR,
+ checkMinFifoLength(
+ Sensor.TYPE_GAME_ROTATION_VECTOR, GAME_ROTATION_VECTOR_MIN_FIFO_LENGTH));
+ }
+
+ public void testProximityFifoLength() throws Throwable {
+ if (!mHasHifiSensors) return;
+ Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (sensor != null) {
+ assertTrue(sensor.getFifoReservedEventCount() <= PROXIMITY_SENSOR_MIN_FIFO_LENGTH);
+ }
+ }
+
+ public void testStepDetectorFifoLength() throws Throwable {
+ if (!mHasHifiSensors) return;
+ checkMinFifoLength(Sensor.TYPE_STEP_DETECTOR, STEP_DETECTOR_MIN_FIFO_LENGTH);
+ }
+
+ private int checkMinFifoLength(int sensorType, int minRequiredLength) {
+ Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
+ assertTrue(String.format("sensor of type=%d (null)", sensorType), sensor != null);
+ int maxFifoLength = sensor.getFifoReservedEventCount();
+ assertTrue(String.format("Sensor=%s, min required fifo length=%d actual=%d",
+ sensor.getName(), minRequiredLength, maxFifoLength),
+ maxFifoLength >= minRequiredLength);
+ return maxFifoLength;
+ }
+
+ private void runBatchingSensorFifoTest(int sensorType, int fifoLength) throws Throwable {
+ if (fifoLength == 0) {
+ return;
+ }
+ Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
+ TestSensorEnvironment environment = new TestSensorEnvironment(getContext(),
+ sensor,
+ false,
+ sensor.getMinDelay(),
+ Integer.MAX_VALUE);
+
+ TestSensorOperation op = TestSensorOperation.createOperation(environment,
+ sensor.getFifoReservedEventCount() * 2);
+ op.addVerification(FifoLengthVerification.getDefault(environment));
+ op.execute(getCurrentTestNode());
+ op.getStats().log(TAG);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index f0f0186..413639a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -41,6 +41,7 @@
public static final String DELIMITER = "__";
public static final String ERROR = "error";
+ public static final String EVENT_FIFO_LENGTH = "event_fifo_length_observed";
public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FifoLengthVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FifoLengthVerification.java
new file mode 100644
index 0000000..09bbdc8
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FifoLengthVerification.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2015 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.helpers.sensorverification;
+
+import junit.framework.Assert;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.util.Log;
+
+import java.util.LinkedList;
+
+/**
+ * A {@link ISensorVerification} which verifies that each batch of events has the FIFO
+ * length within the 5% of the expected value.
+ */
+public class FifoLengthVerification extends AbstractSensorVerification {
+ public static final String PASSED_KEY = "fifo_length_passed";
+
+ private static double FIFO_LENGTH_TOLERANCE = 0.8;
+
+ private final int mExpectedFifoLength;
+
+ private int mIndex = 0;
+ private LinkedList<Long> mRecvdTimeStampDiffs = new LinkedList<>();
+ private long mPrevRecvdTimeStampMs = -1, mExpectedReportLatencyUs;
+
+ /**
+ * Construct a {@link FifoLengthVerification}
+ *
+ * @param expectedLength the expected FIFO length for the batch.
+ */
+ public FifoLengthVerification(int expectedLength, long expectedReportLatencyUs) {
+ mExpectedFifoLength = expectedLength;
+ mExpectedReportLatencyUs = expectedReportLatencyUs;
+ }
+
+ /**
+ * Get the default {@link FifoLengthVerification}.
+ *
+ * @param environment the test environment
+ * @return the verification or null if the verification is not a continuous mode sensor.
+ */
+ public static FifoLengthVerification getDefault(
+ TestSensorEnvironment environment) {
+ if (environment.getSensor().getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
+ return null;
+ }
+ long expectedReportLatencyUs = environment.getMaxReportLatencyUs();
+ long fifoMaxEventCount = environment.getSensor().getFifoMaxEventCount();
+ int maximumExpectedSamplingPeriodUs = environment.getMaximumExpectedSamplingPeriodUs();
+ if (fifoMaxEventCount > 0 && maximumExpectedSamplingPeriodUs != Integer.MAX_VALUE) {
+ long fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+ // If the device goes into suspend mode and the sensor is a non wake-up sensor, the
+ // FIFO will keep overwriting itself and the reportLatency will be equal to the time
+ // it takes to fill up the FIFO.
+ if (environment.isDeviceSuspendTest() && !environment.getSensor().isWakeUpSensor()) {
+ expectedReportLatencyUs = fifoBasedReportLatencyUs;
+ } else {
+ // In this case the sensor under test is either a wake-up sensor OR it
+ // is a non wake-up sensor but the device does not go into suspend.
+ // So the expected delay of a sensor_event is the minimum of the
+ // fifoBasedReportLatencyUs and the requested latency by the application.
+ expectedReportLatencyUs = Math.min(expectedReportLatencyUs,
+ fifoBasedReportLatencyUs);
+ }
+ }
+
+ return new FifoLengthVerification(environment.getSensor().getFifoMaxEventCount(),
+ expectedReportLatencyUs);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void verify(TestSensorEnvironment environment, SensorStats stats) {
+ if (mExpectedFifoLength <= 0) {
+ // the expected length isn't defined.
+ stats.addValue(PASSED_KEY, "skipped (no fifo length requirements)");
+ return;
+ }
+ int batchCount = 0;
+ boolean success, endofbatch = false;
+ long maxTsDiff = -1;
+ for (long timestampDiff : mRecvdTimeStampDiffs) {
+ if (maxTsDiff < timestampDiff) maxTsDiff = timestampDiff;
+ // Any event that arrives within before 0.5*expectedReportLatency is considered
+ // to be in the same batch of events, else it is considered as the beginning of a new
+ // batch.
+ if (timestampDiff < 0.5 * mExpectedReportLatencyUs/1000) {
+ batchCount++;
+ } else {
+ endofbatch = true;
+ break;
+ }
+ }
+ Log.v("SensorFifoLengthVerification", "batchCount =" +batchCount + " mExpected=" +
+ mExpectedFifoLength + " maxTsDiff=" + maxTsDiff + " expectedReportLatency=" +
+ mExpectedReportLatencyUs/1000 + " recvdEventCount=" + mRecvdTimeStampDiffs.size());
+ // Fifo length must be at least 80% of the advertized FIFO length.
+ success = endofbatch && (batchCount >= mExpectedFifoLength * FIFO_LENGTH_TOLERANCE);
+
+ stats.addValue(PASSED_KEY, success);
+ stats.addValue(SensorStats.EVENT_FIFO_LENGTH, batchCount);
+
+ if (!success) {
+ StringBuilder sb = new StringBuilder();
+ if (endofbatch) {
+ sb.append(String.format("Fifo length verification error: Fifo length found=%d," +
+ "expected fifo length ~%d, maxReportLatencyObserved=%dms, " +
+ "expectedMaxReportLantency=%dms",
+ batchCount, mExpectedFifoLength, maxTsDiff,
+ mExpectedReportLatencyUs/1000));
+ } else {
+ sb.append(String.format("End of batch NOT observed maxReportLatencyObserved=%dms,"
+ + " expectedMaxReportLantency=%dms", maxTsDiff,
+ mExpectedReportLatencyUs/1000));
+ }
+ Assert.fail(sb.toString());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FifoLengthVerification clone() {
+ return new FifoLengthVerification(mExpectedFifoLength, mExpectedReportLatencyUs);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void addSensorEventInternal(TestSensorEvent event) {
+ if (mPrevRecvdTimeStampMs == -1) {
+ mPrevRecvdTimeStampMs = (long)event.receivedTimestamp/(1000 * 1000);
+ } else {
+ long currRecvdTimeStampMs = (long) event.receivedTimestamp/(1000 * 1000);
+ mRecvdTimeStampDiffs.add(currRecvdTimeStampMs - mPrevRecvdTimeStampMs);
+ mPrevRecvdTimeStampMs = currRecvdTimeStampMs;
+ }
+ mIndex++;
+ }
+}