Modify tests for barometer

Update barometer MeanVerification according to Android O CDD update and add InitialValueVerification. The CTS pass on 2016 and 2017 devices.
Test: runtest --path cts/tests/sensor/src/android/hardware/cts/helpers/sensorverification
Test: cts-tradefed run cts --module CtsSensorTestCases --test android.hardware.cts
Bug: 38205487

Change-Id: Id9bf5d282a7ff46e55c1148ff64485236ed04ec1
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index 52b3dee..4c8204f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -100,6 +100,7 @@
                 TestSensorOperation.createOperation(environment, 100 /* event count */);
         verifyMeasurements.addVerification(new MeanVerification(
                 expectations,
+                new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */,
                 new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
         verifyMeasurements.execute(getCurrentTestNode());
         return null;
diff --git a/tests/sensor/src/android/hardware/cts/helpers/FrameworkUnitTests.java b/tests/sensor/src/android/hardware/cts/helpers/FrameworkUnitTests.java
index 87a9a2e..6480f55 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/FrameworkUnitTests.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/FrameworkUnitTests.java
@@ -23,6 +23,7 @@
 import android.hardware.cts.helpers.sensorverification.EventGapVerificationTest;
 import android.hardware.cts.helpers.sensorverification.EventOrderingVerificationTest;
 import android.hardware.cts.helpers.sensorverification.FrequencyVerificationTest;
+import android.hardware.cts.helpers.sensorverification.InitialValueVerificationTest;
 import android.hardware.cts.helpers.sensorverification.JitterVerificationTest;
 import android.hardware.cts.helpers.sensorverification.MagnitudeVerificationTest;
 import android.hardware.cts.helpers.sensorverification.MeanVerificationTest;
@@ -42,12 +43,13 @@
         addTestSuite(SensorStatsTest.class);
 
         // sensorverification
+        addTestSuite(EventGapVerificationTest.class);
         addTestSuite(EventOrderingVerificationTest.class);
         addTestSuite(FrequencyVerificationTest.class);
+        addTestSuite(InitialValueVerificationTest.class);
         addTestSuite(JitterVerificationTest.class);
         addTestSuite(MagnitudeVerificationTest.class);
         addTestSuite(MeanVerificationTest.class);
-        addTestSuite(EventGapVerificationTest.class);
         addTestSuite(StandardDeviationVerificationTest.class);
         addTestSuite(TimestampClockSourceVerificationTest.class);
 
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 69ccfb3..06aa815 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -227,6 +227,30 @@
     }
 
     /**
+     * Format an array of floats.
+     *
+     * @param array the array of floats
+     *
+     * @return The formatted string
+     */
+    public static String formatFloatArray(float[] array) {
+        StringBuilder sb = new StringBuilder();
+        if (array.length > 1) {
+            sb.append("(");
+        }
+        for (int i = 0; i < array.length; i++) {
+            sb.append(String.format("%.2f", array[i]));
+            if (i != array.length - 1) {
+                sb.append(", ");
+            }
+        }
+        if (array.length > 1) {
+            sb.append(")");
+        }
+        return sb.toString();
+    }
+
+    /**
      * @return A {@link File} representing a root directory to store sensor tests data.
      */
     public static File getSensorTestDataDirectory() throws IOException {
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
index bc3db99..3892366 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
@@ -64,6 +64,8 @@
     public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
     public static final String MAGNITUDE_KEY = "magnitude";
     public static final String DELAYED_BATCH_DELIVERY = "delayed_batch_delivery";
+    public static final String INITIAL_MEAN_KEY = "initial_mean";
+    public static final String LATER_MEAN_KEY = "later_mean";
 
     private final Map<String, Object> mValues = new HashMap<>();
     private final Map<String, SensorStats> mSensorStats = new HashMap<>();
diff --git a/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 5ef2d3c..8aaadab 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -40,6 +40,7 @@
 import android.hardware.cts.helpers.sensorverification.JitterVerification;
 import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
 import android.hardware.cts.helpers.sensorverification.MeanVerification;
+import android.hardware.cts.helpers.sensorverification.InitialValueVerification;
 import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -110,6 +111,7 @@
         addVerification(MeanVerification.getDefault(mEnvironment));
         addVerification(StandardDeviationVerification.getDefault(mEnvironment));
         addVerification(EventTimestampSynchronizationVerification.getDefault(mEnvironment));
+        addVerification(InitialValueVerification.getDefault(mEnvironment));
     }
 
     public void addVerification(ISensorVerification verification) {
diff --git a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/InitialValueVerification.java b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/InitialValueVerification.java
new file mode 100644
index 0000000..da6a013
--- /dev/null
+++ b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/InitialValueVerification.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 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 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.Pair;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link ISensorVerification} which verifies that there are no ramps when starting the
+ * collection. To verify this, we compute the mean value at the beginning of the collection and
+ * compare it to the mean value at the end of the collection.
+ */
+public class InitialValueVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "initial_value_passed";
+    // Default length of the initial window: 2 seconds in ns
+    private static final long DEFAULT_INITIAL_WINDOW_LENGTH = 2_000_000_000L;
+
+    // sensorType: max absolute delta between the two means and initial window length
+    private static final Map<Integer, Pair<Float, Long>> DEFAULTS =
+        new HashMap<Integer, Pair<Float, Long>>(12);
+
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    // First time stamp in nano seconds
+    private long mFirstTimestamp;
+    private float[] mInitialSum = null;
+    private int mInitialCount = 0;
+    private float[] mLaterSum = null;
+    private int mLaterCount = 0;
+
+    private final float mMaxAbsoluteDelta;
+    private final long mInitialWindowLength;
+
+    /**
+     * Construct a {@link InitialValueVerification}
+     *
+     * @param maxAbsoluteDelta the acceptable max absolute delta between the two means.
+     */
+    public InitialValueVerification(float maxAbsoluteDelta, long initialWindowLength) {
+        mMaxAbsoluteDelta = maxAbsoluteDelta;
+        mInitialWindowLength = initialWindowLength;
+    }
+
+    /**
+     * Get the default {@link InitialValueVerification} for a sensor.
+     *
+     * @param environment the test environment
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static InitialValueVerification getDefault(TestSensorEnvironment environment) {
+        int sensorType = environment.getSensor().getType();
+        if (!DEFAULTS.containsKey(sensorType)) {
+            return null;
+        }
+        Pair<Float, Long> maxAbsoluteDeltaAndInitialWindowLength = DEFAULTS.get(sensorType);
+        return new InitialValueVerification(maxAbsoluteDeltaAndInitialWindowLength.first,
+                                            maxAbsoluteDeltaAndInitialWindowLength.second);
+    }
+
+    /**
+     * Verify that the mean at the initial window and later are similar to each other. Add
+     * {@value #PASSED_KEY}, {@value SensorStats#INITIAL_MEAN_KEY},
+     * {@value SensorStats#LATER_MEAN_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(TestSensorEnvironment environment, SensorStats stats) {
+        verify(stats);
+    }
+
+    /**
+     * Visible for unit tests only.
+     */
+    void verify(SensorStats stats) {
+        if (mInitialCount == 0) {
+            Assert.fail("Didn't collect any measurements");
+        }
+        if (mLaterCount == 0) {
+            Assert.fail(String.format("Didn't collect any measurements after %dns",
+                    mInitialWindowLength));
+        }
+        float[] initialMeans = new float[mInitialSum.length];
+        float[] laterMeans = new float[mInitialSum.length];
+        boolean success = true;
+        for (int i = 0; i < mInitialSum.length; i++) {
+            initialMeans[i] = mInitialSum[i] / mInitialCount;
+            laterMeans[i] = mLaterSum[i] / mLaterCount;
+            if (Math.abs(initialMeans[i] - laterMeans[i]) > mMaxAbsoluteDelta) {
+                success = false;
+            }
+        }
+        stats.addValue(SensorStats.INITIAL_MEAN_KEY, initialMeans);
+        stats.addValue(SensorStats.LATER_MEAN_KEY, laterMeans);
+        stats.addValue(PASSED_KEY, success);
+        if (!success) {
+            Assert.fail(String.format(
+                    "Means too far from each other: initial means = %s,"
+                            + "later means = %s, max allowed delta = %.2f",
+                    SensorCtsHelper.formatFloatArray(initialMeans),
+                    SensorCtsHelper.formatFloatArray(laterMeans),
+                    mMaxAbsoluteDelta));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InitialValueVerification clone() {
+        return new InitialValueVerification(mMaxAbsoluteDelta, mInitialWindowLength);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mInitialSum == null) {
+            mFirstTimestamp = event.timestamp;
+            mInitialSum = new float[event.values.length];
+            mLaterSum = new float[event.values.length];
+        }
+        if (event.timestamp - mFirstTimestamp <= mInitialWindowLength) {
+            for (int i = 0; i < event.values.length; i++) {
+                mInitialSum[i] += event.values[i];
+            }
+            mInitialCount++;
+        } else {
+            for (int i = 0; i < event.values.length; i++) {
+                mLaterSum[i] += event.values[i];
+            }
+            mLaterCount++;
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_ORIENTATION,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        // Very tight absolute delta for the barometer.
+        DEFAULTS.put(Sensor.TYPE_PRESSURE,
+                new Pair<Float, Long>(3f, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_GRAVITY,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+        DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR,
+                new Pair<Float, Long>(Float.MAX_VALUE, DEFAULT_INITIAL_WINDOW_LENGTH));
+    }
+}
+
diff --git a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/InitialValueVerificationTest.java b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/InitialValueVerificationTest.java
new file mode 100644
index 0000000..5e28d26
--- /dev/null
+++ b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/InitialValueVerificationTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 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.TestCase;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Tests for {@link InitialValueVerification}.
+ */
+public class InitialValueVerificationTest extends TestCase {
+    private static final long INITIAL_WINDOW_LENGTH = 2_000_000_000L; // 2s
+    private static final long TOTAL_WINDOW_LENGTH = 5_000_000_000L; // 5s
+    private static final long SENSOR_PERIOD = 500_000_000L; // 0.5s
+    private static final float MAX_ABSOLUTE_DELTA = 3f;
+    private static final Random random = new Random(123L);
+    private static final float NOISE_STD = 0.01f;
+
+    /**
+     * Test {@link InitialValueVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        float[] initialValues = new float[] {80.4f, 12.3f, -67f};
+        verifyStatsWithTwoWindows(initialValues, initialValues, true);
+
+        // Only modify the first element in the array but close enough
+        float[] laterValues = new float[] {78.1f, 12.3f, -67f};
+        verifyStatsWithTwoWindows(initialValues, laterValues, true);
+        // Only modify the first element in the array but by more than the MAX_ABSOLUTE_DELTA
+        laterValues = new float[] {70.1f, 12.3f, -67f};
+        verifyStatsWithTwoWindows(initialValues, laterValues, false);
+
+        // Only modify the second element in the array but close enough
+        laterValues = new float[] {80.4f, 11.3f, -67f};
+        verifyStatsWithTwoWindows(initialValues, laterValues, true);
+        // Only modify the second element in the array but by more than the MAX_ABSOLUTE_DELTA
+        laterValues = new float[] {80.4f, 7.3f, -67f};
+        verifyStatsWithTwoWindows(initialValues, laterValues, false);
+
+        // Only modify the third element in the array but close enough
+        laterValues = new float[] {80.4f, 12.3f, -65f};
+        verifyStatsWithTwoWindows(initialValues, laterValues, true);
+        // Only modify the third element in the array but by more than the MAX_ABSOLUTE_DELTA
+        laterValues = new float[] {80.4f, 12.3f, 45f};
+        verifyStatsWithTwoWindows(initialValues, laterValues, false);
+    }
+
+    private static InitialValueVerification getVerification(Collection<TestSensorEvent> events,
+            float maxAbsoluteDelta, long initialWindowLength) {
+        InitialValueVerification verification =
+                new InitialValueVerification(maxAbsoluteDelta, initialWindowLength);
+        verification.addSensorEvents(events);
+        return verification;
+    }
+
+    private static void verifyStatsWithTwoWindows(float[] initialValues, float[] laterValues,
+            boolean pass) {
+        List<TestSensorEvent> events = new ArrayList<>();
+        // Initial window
+        for (long timestamp = 0L; timestamp <= INITIAL_WINDOW_LENGTH; timestamp += SENSOR_PERIOD) {
+            float[] initialValuesWithNoise = addNoise(initialValues);
+            events.add(new TestSensorEvent(null /* sensor */, timestamp, 0 /* accuracy */,
+                    initialValuesWithNoise));
+        }
+        // Later window
+        for (long timestamp = INITIAL_WINDOW_LENGTH
+                + SENSOR_PERIOD; timestamp <= TOTAL_WINDOW_LENGTH; timestamp += SENSOR_PERIOD) {
+            float[] laterValuesWithNoise = addNoise(laterValues);
+            events.add(new TestSensorEvent(null /* sensor */, timestamp, 0 /* accuracy */,
+                    laterValuesWithNoise));
+        }
+        SensorStats stats = new SensorStats();
+        InitialValueVerification verification =
+                getVerification(events, MAX_ABSOLUTE_DELTA, INITIAL_WINDOW_LENGTH);
+
+        try {
+            verification.verify(stats);
+            assertTrue(pass);
+        } catch (AssertionError e) {
+            assertFalse(pass);
+        }
+        verifyStats(stats, pass, initialValues, laterValues);
+    }
+
+    private static float[] addNoise(float[] values) {
+        float[] valuesWithNoise = new float[values.length];
+        for(int i = 0; i < values.length; i++) {
+            valuesWithNoise[i] = values[i] + random.nextFloat() * NOISE_STD;
+        }
+        return valuesWithNoise;
+    }
+
+    private static void verifyStats(SensorStats stats, boolean passed, float[] initialMeans,
+            float[] laterMeans) {
+        assertEquals(passed, stats.getValue(InitialValueVerification.PASSED_KEY));
+        float[] actualInitialMeans = (float[]) stats.getValue(SensorStats.INITIAL_MEAN_KEY);
+        float[] actualLaterMeans = (float[]) stats.getValue(SensorStats.LATER_MEAN_KEY);
+        assertEquals(initialMeans.length, actualInitialMeans.length);
+        assertEquals(laterMeans.length, actualLaterMeans.length);
+        for (int i = 0; i < initialMeans.length; i++) {
+            assertEquals(initialMeans[i], actualInitialMeans[i], 0.1);
+            assertEquals(laterMeans[i], actualLaterMeans[i], 0.1);
+        }
+    }
+}
diff --git a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java
index 6603895..17882d7 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java
@@ -20,6 +20,7 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorCtsHelper;
 import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 
@@ -33,24 +34,28 @@
     public static final String PASSED_KEY = "mean_passed";
 
     // sensorType: {expected, threshold}
-    private static final Map<Integer, Object[]> DEFAULTS = new HashMap<Integer, Object[]>(5);
+    private static final Map<Integer, ExpectedValuesAndThresholds> DEFAULTS
+        = new HashMap<Integer, ExpectedValuesAndThresholds>(5);
     static {
         // Use a method so that the @deprecation warning can be set for that method only
         setDefaults();
     }
 
     private final float[] mExpected;
-    private final float[] mThreshold;
+    private final float[] mUpperThresholds;
+    private final float[] mLowerThresholds;
 
     /**
      * Construct a {@link MeanVerification}
      *
      * @param expected the expected values
-     * @param threshold the thresholds
+     * @param upperThresholds the upper thresholds
+     * @param lowerThresholds the lower thresholds
      */
-    public MeanVerification(float[] expected, float[] threshold) {
+    public MeanVerification(float[] expected, float[] upperThresholds, float[] lowerThresholds) {
         mExpected = expected;
-        mThreshold = threshold;
+        mUpperThresholds = upperThresholds;
+        mLowerThresholds = lowerThresholds;
     }
 
     /**
@@ -64,9 +69,10 @@
         if (!DEFAULTS.containsKey(sensorType)) {
             return null;
         }
-        float[] expected = (float[]) DEFAULTS.get(sensorType)[0];
-        float[] threshold = (float[]) DEFAULTS.get(sensorType)[1];
-        return new MeanVerification(expected, threshold);
+        float[] expected = DEFAULTS.get(sensorType).mExpectedValues;
+        float[] upperThresholds = DEFAULTS.get(sensorType).mUpperThresholds;
+        float[] lowerThresholds = DEFAULTS.get(sensorType).mLowerThresholds;
+        return new MeanVerification(expected, upperThresholds, lowerThresholds);
     }
 
     /**
@@ -92,66 +98,104 @@
         float[] means = getMeans();
 
         boolean failed = false;
-        StringBuilder meanSb = new StringBuilder();
-        StringBuilder expectedSb = new StringBuilder();
-
-        if (means.length > 1) {
-            meanSb.append("(");
-            expectedSb.append("(");
-        }
         for (int i = 0; i < means.length; i++) {
-            if (Math.abs(means[i] - mExpected[i]) > mThreshold[i]) {
+            if (means[i]  > mExpected[i] + mUpperThresholds[i]) {
                 failed = true;
             }
-            meanSb.append(String.format("%.2f", means[i]));
-            if (i != means.length - 1) meanSb.append(", ");
-            expectedSb.append(String.format("%.2f+/-%.2f", mExpected[i], mThreshold[i]));
-            if (i != means.length - 1) expectedSb.append(", ");
-        }
-        if (means.length > 1) {
-            meanSb.append(")");
-            expectedSb.append(")");
+            if (means[i] < mExpected[i] - mLowerThresholds[i]) {
+                failed = true;
+            }
         }
 
         stats.addValue(PASSED_KEY, !failed);
         stats.addValue(SensorStats.MEAN_KEY, means);
 
         if (failed) {
-            Assert.fail(String.format("Mean out of range: mean=%s (expected %s)", meanSb.toString(),
-                    expectedSb.toString()));
+            Assert.fail(String.format("Mean out of range: mean=%s (expected %s)",
+                    SensorCtsHelper.formatFloatArray(means),
+                    SensorCtsHelper.formatFloatArray(mExpected)));
         }
     }
 
     @Override
     public MeanVerification clone() {
-        return new MeanVerification(mExpected, mThreshold);
+        return new MeanVerification(mExpected, mUpperThresholds, mLowerThresholds);
     }
 
     @SuppressWarnings("deprecation")
     private static void setDefaults() {
         // Sensors that we don't want to test at this time but still want to record the values.
         // Gyroscope should be 0 for a static device
-        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new Object[]{
-                new float[]{0.0f, 0.0f, 0.0f},
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE}});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE,
+            new ExpectedValuesAndThresholds(new float[]{0.0f, 0.0f, 0.0f},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE}));
         // Pressure will not be exact in a controlled environment but should be relatively close to
-        // sea level. Second values should always be 0.
-        DEFAULTS.put(Sensor.TYPE_PRESSURE, new Object[]{
-                new float[]{SensorManager.PRESSURE_STANDARD_ATMOSPHERE, 0.0f, 0.0f},
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE}});
+        // sea level (400HPa and 200HPa are very lax thresholds).
+        // Second values should always be 0.
+        DEFAULTS.put(Sensor.TYPE_PRESSURE,
+            new ExpectedValuesAndThresholds(new float[]{SensorManager.PRESSURE_STANDARD_ATMOSPHERE,
+                                                        0.0f,
+                                                        0.0f},
+                                            new float[]{100f,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE},
+                                            new float[]{400f,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE}));
         // Linear acceleration should be 0 in all directions for a static device
-        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION, new Object[]{
-                new float[]{0.0f, 0.0f, 0.0f},
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE}});
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION,
+            new ExpectedValuesAndThresholds(new float[]{0.0f, 0.0f, 0.0f},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE}));
         // Game rotation vector should be (0, 0, 0, 1, 0) for a static device
-        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR, new Object[]{
-                new float[]{0.0f, 0.0f, 0.0f, 1.0f, 0.0f},
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
-                        Float.MAX_VALUE}});
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR,
+            new ExpectedValuesAndThresholds(new float[]{0.0f, 0.0f, 0.0f, 1.0f, 0.0f},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE}));
         // Uncalibrated gyroscope should be 0 for a static device but allow a bigger threshold
-        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, new Object[]{
-                new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
-                        Float.MAX_VALUE, Float.MAX_VALUE}});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
+            new ExpectedValuesAndThresholds(new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE},
+                                            new float[]{Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE,
+                                                        Float.MAX_VALUE}));
+    }
+
+    private static final class ExpectedValuesAndThresholds {
+        private float[] mExpectedValues;
+        private float[] mUpperThresholds;
+        private float[] mLowerThresholds;
+        private ExpectedValuesAndThresholds(float[] expectedValues,
+                                            float[] upperThresholds,
+                                            float[] lowerThresholds) {
+            mExpectedValues = expectedValues;
+            mUpperThresholds = upperThresholds;
+            mLowerThresholds = lowerThresholds;
+        }
     }
 }
diff --git a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
index 64aef60..6661e78 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
@@ -29,6 +29,7 @@
  * Tests for {@link MeanVerification}.
  */
 public class MeanVerificationTest extends TestCase {
+    private static final float[] MEANS = {2.0f, 3.0f, 6.0f};
 
     /**
      * Test {@link MeanVerification#verify(TestSensorEnvironment, SensorStats)}.
@@ -43,62 +44,106 @@
         };
 
         float[] expected = {2.0f, 3.0f, 6.0f};
-        float[] threshold = {0.1f, 0.1f, 0.1f};
+        float[] upperThresholds = {0.3f, 0.3f, 0.3f};
+        float[] lowerThresholds = {0.1f, 0.1f, 0.1f};
         SensorStats stats = new SensorStats();
-        MeanVerification verification = getVerification(expected, threshold, values);
+        MeanVerification verification =
+            getVerification(expected, upperThresholds, lowerThresholds, values);
         verification.verify(stats);
-        verifyStats(stats, true, new float[]{2.0f, 3.0f, 6.0f});
+        verifyStats(stats, true, MEANS);
 
-        expected = new float[]{2.5f, 2.5f, 5.5f};
-        threshold = new float[]{0.6f, 0.6f, 0.6f};
+        // Test the lower threshold
+        expected = new float[]{2.4f, 3.3f, 6.4f};
+        lowerThresholds = new float[]{0.6f, 0.6f, 0.6f};
         stats = new SensorStats();
-        verification = getVerification(expected, threshold, values);
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
         verification.verify(stats);
-        verifyStats(stats, true, new float[]{2.0f, 3.0f, 6.0f});
+        verifyStats(stats, true, MEANS);
 
-        expected = new float[]{2.5f, 2.5f, 5.5f};
-        threshold = new float[]{0.1f, 0.6f, 0.6f};
+        lowerThresholds = new float[]{0.1f, 0.6f, 0.6f};
         stats = new SensorStats();
-        verification = getVerification(expected, threshold, values);
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
         try {
             verification.verify(stats);
             throw new Error("Expected an AssertionError");
         } catch (AssertionError e) {
             // Expected;
         }
-        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+        verifyStats(stats, false, MEANS);
 
-        expected = new float[]{2.5f, 2.5f, 5.5f};
-        threshold = new float[]{0.6f, 0.1f, 0.6f};
+        lowerThresholds = new float[]{0.6f, 0.1f, 0.6f};
         stats = new SensorStats();
-        verification = getVerification(expected, threshold, values);
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
         try {
             verification.verify(stats);
             throw new Error("Expected an AssertionError");
         } catch (AssertionError e) {
             // Expected;
         }
-        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+        verifyStats(stats, false, MEANS);
 
-        threshold = new float[]{0.6f, 0.6f, 0.1f};
+        lowerThresholds = new float[]{0.6f, 0.6f, 0.1f};
         stats = new SensorStats();
-        verification = getVerification(expected, threshold, values);
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
         try {
             verification.verify(stats);
             throw new Error("Expected an AssertionError");
         } catch (AssertionError e) {
             // Expected;
         }
-        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+        verifyStats(stats, false, MEANS);
+
+        // Test the upper threshold
+        expected = new float[]{1.5f, 2.8f, 5.7f};
+        upperThresholds = new float[]{0.6f, 0.6f, 0.6f};
+        lowerThresholds = new float[]{0.1f, 0.1f, 0.1f};
+        stats = new SensorStats();
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
+        verification.verify(stats);
+        verifyStats(stats, true, MEANS);
+
+        upperThresholds = new float[]{0.1f, 0.6f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
+        try {
+            verification.verify(stats);
+            throw new Error("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, MEANS);
+
+        upperThresholds = new float[]{0.6f, 0.1f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
+        try {
+            verification.verify(stats);
+            throw new Error("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, MEANS);
+
+        upperThresholds = new float[]{0.6f, 0.6f, 0.1f};
+        stats = new SensorStats();
+        verification = getVerification(expected, upperThresholds, lowerThresholds, values);
+        try {
+            verification.verify(stats);
+            throw new Error("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, MEANS);
     }
 
-    private static MeanVerification getVerification(float[] expected, float[] threshold,
-            float[] ... values) {
+    private static MeanVerification getVerification(float[] expected, float[] upperThresholds,
+            float[] lowerThresholds, float[] ... values) {
         Collection<TestSensorEvent> events = new ArrayList<>(values.length);
         for (float[] value : values) {
             events.add(new TestSensorEvent(null, 0, 0, value));
         }
-        MeanVerification verification = new MeanVerification(expected, threshold);
+        MeanVerification verification =
+            new MeanVerification(expected, upperThresholds, lowerThresholds);
         verification.addSensorEvents(events);
         return verification;
     }
@@ -106,6 +151,7 @@
     private void verifyStats(SensorStats stats, boolean passed, float[] means) {
         assertEquals(passed, stats.getValue(MeanVerification.PASSED_KEY));
         float[] actual = (float[]) stats.getValue(SensorStats.MEAN_KEY);
+        assertEquals(means.length, actual.length);
         for (int i = 0; i < means.length; i++) {
             assertEquals(means[i], actual[i], 0.1);
         }