Allow for long running operations.

Refactor sensor verifications to allow for running verifications on the
fly.  This changes how the sensor manager works, moves the test sensor
event listener to a separate class, and breaks up the verifications into
individual classes for more flexibility.

Change-Id: If712edb589206adce95347de2e0fe975fcaff188
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 44bed6c..5a0af28 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -18,7 +18,8 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensoroperations.VerifySensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.MeanVerification;
 
 import java.util.concurrent.TimeUnit;
 
@@ -74,15 +75,15 @@
      */
     private void verifyMeasurements(float ... expectations) throws Throwable {
         Thread.sleep(500 /*ms*/);
-        VerifySensorOperation verifyMeasurements = new VerifySensorOperation(
+        TestSensorOperation verifyMeasurements = new TestSensorOperation(
                 getApplicationContext(),
                 Sensor.TYPE_ACCELEROMETER,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
                 100 /* event count */);
-        verifyMeasurements.verifyMean(
+        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();
         logSuccess();
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index cdb5b3f..066bda4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -18,7 +18,8 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensoroperations.VerifySensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.SigNumVerification;
 
 /**
  * Semi-automated test that focuses on characteristics associated with Accelerometer measurements.
@@ -96,15 +97,15 @@
         waitForUser();
 
         Thread.sleep(500 /*ms*/);
-        VerifySensorOperation verifySignum = new VerifySensorOperation(
+        TestSensorOperation verifySignum = new TestSensorOperation(
                 getApplicationContext(),
                 Sensor.TYPE_GYROSCOPE,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
                 100 /* event count */);
-        verifySignum.verifySignum(
+        verifySignum.addVerification(new SigNumVerification(
                 expectations,
-                new float[]{0.2f, 0.2f, 0.2f} /*noiseThreshold*/);
+                new float[]{0.2f, 0.2f, 0.2f} /*noiseThreshold*/));
         verifySignum.execute();
         logSuccess();
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index d36c998..a131b2b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -21,8 +21,11 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
+import android.hardware.cts.helpers.TestSensorEventListener;
 import android.hardware.cts.helpers.TestSensorManager;
-import android.hardware.cts.helpers.sensoroperations.VerifySensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
 
 /**
  * Semi-automated test that focuses characteristics associated with Accelerometer measurements.
@@ -64,9 +67,9 @@
 
         TestSensorManager magnetometer = new TestSensorManager(
                 this.getApplicationContext(), Sensor.TYPE_MAGNETIC_FIELD,
-                SensorManager.SENSOR_DELAY_NORMAL, 0, listener);
+                SensorManager.SENSOR_DELAY_NORMAL, 0);
         try {
-            magnetometer.registerListener();
+            magnetometer.registerListener(new TestSensorEventListener(listener));
             waitForUser();
         } finally {
             magnetometer.unregisterListener();
@@ -98,15 +101,15 @@
                 (SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
         float magneticFieldEarthThreshold =
                 expectedMagneticFieldEarth - SensorManager.MAGNETIC_FIELD_EARTH_MIN;
-        VerifySensorOperation verifyNorm = new VerifySensorOperation(
+        TestSensorOperation verifyNorm = new TestSensorOperation(
                 this.getApplicationContext(),
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
                 100 /* event count */);
-        verifyNorm.verifyMagnitude(
+        verifyNorm.addVerification(new MagnitudeVerification(
                 expectedMagneticFieldEarth,
-                magneticFieldEarthThreshold);
+                magneticFieldEarthThreshold));
         verifyNorm.execute();
         logSuccess();
     }
@@ -135,14 +138,14 @@
      * the failure to help track down the issue.
      */
     private void verifyStandardDeviation() throws Throwable {
-        VerifySensorOperation verifyStdDev = new VerifySensorOperation(
+        TestSensorOperation verifyStdDev = new TestSensorOperation(
                 this.getApplicationContext(),
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
                 100 /* event count */);
-        verifyStdDev.verifyStandardDeviation(
-                new float[]{2f, 2f, 2f} /* uT */);
+        verifyStdDev.addVerification(new StandardDeviationVerification(
+                new float[]{2f, 2f, 2f} /* uT */));
         verifyStdDev.execute();
         logSuccess();
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index bcf9755..c27d108 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -25,7 +25,8 @@
 import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
 import android.hardware.cts.helpers.sensoroperations.RepeatingSensorOperation;
 import android.hardware.cts.helpers.sensoroperations.SequentialSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifySensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
@@ -96,22 +97,22 @@
 
         ParallelSensorOperation operation = new ParallelSensorOperation();
         for(int sensorType : sensorTypes) {
-            VerifySensorOperation continuousOperation = new VerifySensorOperation(
+            TestSensorOperation continuousOperation = new TestSensorOperation(
                     context,
                     sensorType,
                     SensorManager.SENSOR_DELAY_NORMAL,
                     0 /* reportLatencyInUs */,
                     100 /* event count */);
-            continuousOperation.verifyEventOrdering();
+            continuousOperation.addVerification(new EventOrderingVerification());
             operation.add(new RepeatingSensorOperation(continuousOperation, ITERATIONS));
 
-            VerifySensorOperation batchingOperation = new VerifySensorOperation(
+            TestSensorOperation batchingOperation = new TestSensorOperation(
                     context,
                     sensorType,
                     SensorTestInformation.getMaxSamplingRateInUs(context, sensorType),
                     SensorCtsHelper.getSecondsAsMicroSeconds(BATCHING_RATE_IN_SECONDS),
                     100);
-            batchingOperation.verifyEventOrdering();
+            batchingOperation.addVerification(new EventOrderingVerification());
             operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
         }
         operation.execute();
@@ -154,13 +155,13 @@
             for(int instance = 0; instance < INSTANCES_TO_USE; ++instance) {
                 SequentialSensorOperation sequentialOperation = new SequentialSensorOperation();
                 for(int iteration = 0; iteration < ITERATIONS_TO_EXECUTE; ++iteration) {
-                    VerifySensorOperation sensorOperation = new VerifySensorOperation(
+                    TestSensorOperation sensorOperation = new TestSensorOperation(
                             this.getContext(),
                             sensorType,
                             this.generateSamplingRateInUs(sensorType),
                             this.generateReportLatencyInUs(),
                             100);
-                    sensorOperation.verifyEventOrdering();
+                    sensorOperation.addVerification(new EventOrderingVerification());
                     sequentialOperation.add(sensorOperation);
                 }
                 operation.add(sequentialOperation);
@@ -218,21 +219,21 @@
     public void testSensorStoppingInteraction() throws Throwable {
         Context context = this.getContext();
 
-        VerifySensorOperation tester = new VerifySensorOperation(
+        TestSensorOperation tester = new TestSensorOperation(
                 context,
                 mSensorTypeTester,
                 SensorManager.SENSOR_DELAY_NORMAL,
                 0 /*reportLatencyInUs*/,
                 100 /* event count */);
-        tester.verifyEventOrdering();
+        tester.addVerification(new EventOrderingVerification());
 
-        VerifySensorOperation testee = new VerifySensorOperation(
+        TestSensorOperation testee = new TestSensorOperation(
                 context,
                 mSensorTypeTestee,
                 SensorManager.SENSOR_DELAY_UI,
                 0 /*reportLatencyInUs*/,
                 100 /* event count */);
-        testee.verifyEventOrdering();
+        testee.addVerification(new EventOrderingVerification());
 
         ParallelSensorOperation operation = new ParallelSensorOperation();
         operation.add(tester, testee);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index 8bdc161..647e7dc 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -22,11 +22,13 @@
 import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.SensorTestCase;
 import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.sensoroperations.VerifySensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.util.Log;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Set of tests to verify that sensors operate correctly when operating alone.
@@ -214,12 +216,21 @@
                 minDelay * 8,
                 minDelay * 16,
         };
+        int[] maxBatchReportLatencyUss = {
+                0, // No batching
+                (int) TimeUnit.MICROSECONDS.convert(1, TimeUnit.SECONDS),
+                (int) TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS),
+        };
         for (int rateUs : rateUss) {
-            VerifySensorOperation op = new VerifySensorOperation(this.getContext(), sensorType,
-                    rateUs, 0, 100);
-            op.setDefaultVerifications();
-            op.execute();
-            SensorStats.logStats(TAG, op.getStats());
+            for (int maxBatchReportLatencyUs : maxBatchReportLatencyUss) {
+                Log.v(TAG, String.format("Run on %d with rate %d and batch %d", sensorType, rateUs,
+                        maxBatchReportLatencyUs));
+                TestSensorOperation op = new TestSensorOperation(this.getContext(), sensorType,
+                        rateUs, maxBatchReportLatencyUs, 1, TimeUnit.SECONDS);
+                op.setDefaultVerifications();
+                op.execute();
+                SensorStats.logStats(TAG, op.getStats());
+            }
         }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
new file mode 100644
index 0000000..981d74c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.os.SystemClock;
+
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link TestSensorEventListener} which collects events to be processed after the test is run.
+ * This should only be used for short tests.
+ */
+public class CollectingSensorEventListener extends TestSensorEventListener {
+    private final ConcurrentLinkedDeque<TestSensorEvent> mSensorEventsList =
+            new ConcurrentLinkedDeque<TestSensorEvent>();
+
+    /**
+     * Constructs a {@link CollectingSensorEventListener} with an additional
+     * {@link SensorEventListener2}.
+     */
+    public CollectingSensorEventListener(SensorEventListener2 listener) {
+        super(listener);
+    }
+
+    /**
+     * Constructs a {@link CollectingSensorEventListener}.
+     */
+    public CollectingSensorEventListener() {
+        this(null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        super.onSensorChanged(event);
+        mSensorEventsList.addLast(new TestSensorEvent(event, SystemClock.elapsedRealtimeNanos()));
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Clears the event queue before starting.
+     * </p>
+     */
+    @Override
+    public void waitForEvents(int eventCount) {
+        clearEvents();
+        super.waitForEvents(eventCount);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Clears the event queue before starting.
+     * </p>
+     */
+    @Override
+    public void waitForEvents(long duration, TimeUnit timeUnit) {
+        clearEvents();
+        super.waitForEvents(duration, timeUnit);
+    }
+
+    /**
+     * Get the {@link TestSensorEvent} array from the event queue.
+     */
+    public TestSensorEvent[] getEvents() {
+        return mSensorEventsList.toArray(new TestSensorEvent[0]);
+    }
+
+    /**
+     * Clear the event queue.
+     */
+    public void clearEvents() {
+        mSensorEventsList.clear();
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/FrameworkUnitTests.java b/tests/tests/hardware/src/android/hardware/cts/helpers/FrameworkUnitTests.java
new file mode 100644
index 0000000..872bf28
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/FrameworkUnitTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.cts.helpers.sensoroperations.SensorOperationTest;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerificationTest;
+import android.hardware.cts.helpers.sensorverification.FrequencyVerificationTest;
+import android.hardware.cts.helpers.sensorverification.JitterVerificationTest;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerificationTest;
+import android.hardware.cts.helpers.sensorverification.MeanVerificationTest;
+import android.hardware.cts.helpers.sensorverification.SigNumVerificationTest;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerificationTest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test suite for the CTS sensor framework.
+ */
+public class FrameworkUnitTests extends TestSuite {
+
+    public FrameworkUnitTests() {
+        super();
+
+        // helpers
+        addTestSuite(SensorCtsHelperTest.class);
+        addTestSuite(SensorStatsTest.class);
+
+        // sensorverification
+        addTestSuite(EventOrderingVerificationTest.class);
+        addTestSuite(FrequencyVerificationTest.class);
+        addTestSuite(JitterVerificationTest.class);
+        addTestSuite(MagnitudeVerificationTest.class);
+        addTestSuite(MeanVerificationTest.class);
+        addTestSuite(SigNumVerificationTest.class);
+        addTestSuite(StandardDeviationVerificationTest.class);
+
+        // sensorOperations
+        addTestSuite(SensorOperationTest.class);
+    }
+
+    public static Test suite() {
+        return new FrameworkUnitTests();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 9f281b9..65e0072 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -56,72 +56,6 @@
     }
 
     /**
-     * Calculates the mean for each of the values in the set of TestSensorEvents.
-     *
-     * @throws IllegalArgumentException if there are no events
-     */
-    public static Float[] getMeans(TestSensorEvent[] events) {
-        if (events.length == 0) {
-            throw new IllegalArgumentException("Events cannot be empty");
-        }
-
-        Float[] means = new Float[events[0].values.length];
-        for (int i = 0; i < means.length; i++) {
-            means[i] = 0.0f;
-        }
-        for (TestSensorEvent event : events) {
-            for (int i = 0; i < means.length; i++) {
-                means[i] += event.values[i];
-            }
-        }
-        for (int i = 0; i < means.length; i++) {
-            means[i] /= events.length;
-        }
-        return means;
-    }
-
-    /**
-     * Calculates the bias-corrected variance for each of the values in the set of TestSensorEvents.
-     *
-     * @throws IllegalArgumentException if there are no events
-     */
-    public static Float[] getVariances(TestSensorEvent[] events) {
-        Float[] means = getMeans(events);
-        Float[] variances = new Float[means.length];
-        for (int i = 0; i < variances.length; i++) {
-            variances[i] = 0.0f;
-        }
-        for (int i = 0; i < means.length; i++) {
-            Collection<Float> squaredDiffs = new ArrayList<Float>(events.length);
-            for (TestSensorEvent event : events) {
-                float diff = event.values[i] - means[i];
-                squaredDiffs.add(diff * diff);
-            }
-            float sum = 0.0f;
-            for (float value : squaredDiffs) {
-                sum += value;
-            }
-            variances[i] = sum / (events.length - 1);
-        }
-        return variances;
-    }
-
-    /**
-     * Calculates the bias-corrected standard deviation for each of the values in the set of
-     * TestSensorEvents.
-     *
-     * @throws IllegalArgumentException if there are no events
-     */
-    public static Float[] getStandardDeviations(TestSensorEvent[] events) {
-        Float[] variances = getVariances(events);
-        Float[] stdDevs = new Float[variances.length];
-        for (int i = 0; i < variances.length; i++) {
-            stdDevs[i] = (float) Math.sqrt(variances[i]);
-        }
-        return stdDevs;
-    }
-
-    /**
      * Calculate the mean of a collection.
      *
      * @throws IllegalArgumentException if the collection is null or empty
@@ -169,41 +103,6 @@
     }
 
     /**
-     * Get a list containing the delay between sensor events.
-     *
-     * @param events The array of {@link TestSensorEvent}.
-     * @return A list containing the delay between sensor events in nanoseconds.
-     */
-    public static List<Long> getTimestampDelayValues(TestSensorEvent[] events) {
-        if (events.length < 2) {
-            return new ArrayList<Long>();
-        }
-        List<Long> timestampDelayValues = new ArrayList<Long>(events.length - 1);
-        for (int i = 1; i < events.length; i++) {
-            timestampDelayValues.add(events[i].timestamp - events[i - 1].timestamp);
-        }
-        return timestampDelayValues;
-    }
-
-    /**
-     * Get a list containing the jitter values for a collection of sensor events.
-     *
-     * @param events The array of {@link TestSensorEvent}.
-     * @return A list containing the jitter values between each event.
-     * @throws IllegalArgumentException if the number of events is less that 2.
-     */
-    public static List<Double> getJitterValues(TestSensorEvent[] events) {
-        List<Long> timestampDelayValues = getTimestampDelayValues(events);
-        double averageTimestampDelay = getMean(timestampDelayValues);
-
-        List<Double> jitterValues = new ArrayList<Double>(timestampDelayValues.size());
-        for (long timestampDelay : timestampDelayValues) {
-            jitterValues.add(Math.abs(timestampDelay - averageTimestampDelay));
-        }
-        return jitterValues;
-    }
-
-    /**
      * Get the default sensor for a given type.
      */
     public static Sensor getSensor(Context context, int sensorType) {
@@ -279,6 +178,16 @@
     }
 
     /**
+     * Return true if the operation rate is not one of {@link SensorManager#SENSOR_DELAY_GAME},
+     * {@link SensorManager#SENSOR_DELAY_UI}, or {@link SensorManager#SENSOR_DELAY_NORMAL}.
+     */
+    public boolean isDelayRateTestable(int rateUs) {
+        return (rateUs != SensorManager.SENSOR_DELAY_GAME
+                && rateUs != SensorManager.SENSOR_DELAY_UI
+                && rateUs != SensorManager.SENSOR_DELAY_NORMAL);
+    }
+
+    /**
      * Helper method to sleep for a given duration.
      */
     public static void sleep(long duration, TimeUnit timeUnit) {
@@ -291,6 +200,34 @@
     }
 
     /**
+     * Format an assertion message.
+     *
+     * @param sensor the {@link Sensor}
+     * @param label the verification name
+     * @return The formatted string
+     */
+    public static String formatAssertionMessage(Sensor sensor, String label) {
+        return String.format("%s | %s, handle: %d", label,
+                SensorTestInformation.getSensorName(sensor.getType()), sensor.getHandle());
+    }
+
+    /**
+     * Format an assertion message with a custom message.
+     *
+     * @param sensor the {@link Sensor}
+     * @param label the verification name
+     * @param format the additional format string
+     * @param params the additional format params
+     * @return The formatted string
+     */
+    public static String formatAssertionMessage(Sensor sensor, String label, String format,
+            Object ... params) {
+        return String.format("%s | %s, handle: %d | %s", label,
+                SensorTestInformation.getSensorName(sensor.getType()), sensor.getHandle(),
+                String.format(format, params));
+    }
+
+    /**
      * Validate that a collection is not null or empty.
      *
      * @throws IllegalStateException if collection is null or empty.
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
index b8cb06f..6f99692 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
@@ -53,73 +53,6 @@
     }
 
     /**
-     * Test {@link SensorCtsHelper#getMeans(TestSensorEvent[])}.
-     */
-    public void testGetMeans() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-
-        float[] values = {0, 1, 2, 3, 4};  // 2.0
-        Float[] means = SensorCtsHelper.getMeans(getSensorEvents(timestamps, values));
-        assertEquals(1, means.length);
-        assertEquals(2.0, means[0], 0.00001);
-
-        float[] values1 = {0, 1, 2, 3, 4};  // 2.0
-        float[] values2 = {1, 2, 3, 4, 5};  // 3.0
-        float[] values3 = {0, 1, 4, 9, 16};  // 6.0
-        means = SensorCtsHelper.getMeans(
-                getSensorEvents(timestamps, values1, values2, values3));
-        assertEquals(3, means.length);
-        assertEquals(2.0, means[0], 0.00001);
-        assertEquals(3.0, means[1], 0.00001);
-        assertEquals(6.0, means[2], 0.00001);
-    }
-
-    /**
-     * Test {@link SensorCtsHelper#getVariances(TestSensorEvent[])}.
-     */
-    public void testGetVariences() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-
-        float[] values = {0, 1, 2, 3, 4};  // 2.5
-        Float[] variances = SensorCtsHelper.getVariances(getSensorEvents(timestamps, values));
-        assertEquals(1, variances.length);
-        assertEquals(2.5, variances[0], 0.00001);
-
-        float[] values1 = {0, 1, 2, 3, 4};  // 2.5
-        float[] values2 = {1, 2, 3, 4, 5};  // 2.5
-        float[] values3 = {0, 2, 4, 6, 8};  // 10.0
-        variances = SensorCtsHelper.getVariances(
-                getSensorEvents(timestamps, values1, values2, values3));
-        assertEquals(3, variances.length);
-        assertEquals(2.5, variances[0], 0.00001);
-        assertEquals(2.5, variances[1], 0.00001);
-        assertEquals(10.0, variances[2], 0.00001);
-    }
-
-    /**
-     * Test {@link SensorCtsHelper#getStandardDeviations(TestSensorEvent[])}.
-     */
-    public void testGetStandardDeviations() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-
-        float[] values = {0, 1, 2, 3, 4};  // sqrt(2.5)
-        Float[] stddev = SensorCtsHelper.getStandardDeviations(
-                getSensorEvents(timestamps, values));
-        assertEquals(1, stddev.length);
-        assertEquals(Math.sqrt(2.5), stddev[0], 0.00001);
-
-        float[] values1 = {0, 1, 2, 3, 4};  // sqrt(2.5)
-        float[] values2 = {1, 2, 3, 4, 5};  // sqrt(2.5)
-        float[] values3 = {0, 2, 4, 6, 8};  // sqrt(10.0)
-        stddev = SensorCtsHelper.getStandardDeviations(
-                getSensorEvents(timestamps, values1, values2, values3));
-        assertEquals(3, stddev.length);
-        assertEquals(Math.sqrt(2.5), stddev[0], 0.00001);
-        assertEquals(Math.sqrt(2.5), stddev[1], 0.00001);
-        assertEquals(Math.sqrt(10.0), stddev[2], 0.00001);
-    }
-
-    /**
      * Test {@link SensorCtsHelper#getMean(Collection)}.
      */
     public void testGetMean() {
@@ -171,22 +104,6 @@
     }
 
     /**
-     * Test {@link SensorCtsHelper#getTimestampDelayValues(TestSensorEvent[])}.
-     */
-    public void testGetTimestampDelayValues() {
-        float[] values = {0, 1, 2, 3, 4};
-
-        long[] timestamps = {0, 0, 1, 3, 100};
-        List<Long> timestampDelayValues = SensorCtsHelper.getTimestampDelayValues(
-                getSensorEvents(timestamps, values));
-        assertEquals(4, timestampDelayValues.size());
-        assertEquals(0, (long) timestampDelayValues.get(0));
-        assertEquals(1, (long) timestampDelayValues.get(1));
-        assertEquals(2, (long) timestampDelayValues.get(2));
-        assertEquals(97, (long) timestampDelayValues.get(3));
-    }
-
-    /**
      * Test {@link SensorCtsHelper#getFrequency(Number, TimeUnit)}.
      */
     public void testGetFrequency() {
@@ -217,50 +134,4 @@
         assertEquals(100, SensorCtsHelper.getPeriod(10000000, TimeUnit.NANOSECONDS), 0.001);
         assertEquals(1, SensorCtsHelper.getPeriod(1000000000, TimeUnit.NANOSECONDS), 0.001);
     }
-
-    /**
-     * Test {@link SensorCtsHelper#getJitterValues(TestSensorEvent[])}.
-     */
-    public void testGetJitterValues() {
-        float[] values = {0, 1, 2, 3, 4};
-
-        long[] timestamps1 = {0, 1, 2, 3, 4};
-        List<Double> jitterValues = SensorCtsHelper.getJitterValues(
-                getSensorEvents(timestamps1, values));
-        assertEquals(4, jitterValues.size());
-        assertEquals(0.0, (double) jitterValues.get(0));
-        assertEquals(0.0, (double) jitterValues.get(1));
-        assertEquals(0.0, (double) jitterValues.get(2));
-        assertEquals(0.0, (double) jitterValues.get(3));
-
-        long[] timestamps2 = {0, 0, 2, 4, 4};
-        jitterValues = SensorCtsHelper.getJitterValues(
-                getSensorEvents(timestamps2, values));
-        assertEquals(4, jitterValues.size());
-        assertEquals(1.0, (double) jitterValues.get(0));
-        assertEquals(1.0, (double) jitterValues.get(1));
-        assertEquals(1.0, (double) jitterValues.get(2));
-        assertEquals(1.0, (double) jitterValues.get(3));
-
-        long[] timestamps3 = {0, 1, 4, 9, 16};
-        jitterValues = SensorCtsHelper.getJitterValues(
-                getSensorEvents(timestamps3, values));
-        assertEquals(4, jitterValues.size());
-        assertEquals(3.0, (double) jitterValues.get(0));
-        assertEquals(1.0, (double) jitterValues.get(1));
-        assertEquals(1.0, (double) jitterValues.get(2));
-        assertEquals(3.0, (double) jitterValues.get(3));
-    }
-
-    private TestSensorEvent[] getSensorEvents(long[] timestamps, float[] ... values) {
-        TestSensorEvent[] events = new TestSensorEvent[timestamps.length];
-        for (int i = 0; i < timestamps.length; i++) {
-            float[] eventValues = new float[values.length];
-            for (int j = 0; j < values.length; j++) {
-                eventValues[j] = values[j][i];
-            }
-            events[i] = new TestSensorEvent(null, timestamps[i], 0, eventValues);
-        }
-        return events;
-    }
 }
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 48f8136..1898570 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -20,11 +20,13 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * Class used to store stats related to {@link ISensorOperation}s.  Sensor stats may be linked
@@ -33,6 +35,17 @@
 public class SensorStats {
     public static final String DELIMITER = "__";
 
+    public static final String FIRST_TIMESTAMP_KEY = "first_timestamp";
+    public static final String LAST_TIMESTAMP_KEY = "last_timestamp";
+    public static final String EVENT_COUNT_KEY = "event_count";
+    public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
+    public static final String EVENT_OUT_OF_ORDER_POSITIONS_KEY = "event_out_of_order_positions";
+    public static final String FREQUENCY_KEY = "frequency";
+    public static final String JITTER_95_PERCENTILE_KEY = "jitter_95_percentile";
+    public static final String MEAN_KEY = "mean";
+    public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
+    public static final String MAGNITUDE_KEY = "magnitude";
+
     private final Map<String, Object> mValues = new HashMap<String, Object>();
     private final Map<String, SensorStats> mSensorStats = new HashMap<String, SensorStats>();
 
@@ -64,6 +77,22 @@
     }
 
     /**
+     * Get the keys from the values table. Will not get the keys from the nested
+     * {@link SensorStats}.
+     */
+    public synchronized Set<String> getKeys() {
+        return mValues.keySet();
+    }
+
+    /**
+     * Get a value from the values table. Will not attempt to get values from nested
+     * {@link SensorStats}.
+     */
+    public synchronized Object getValue(String key) {
+        return mValues.get(key);
+    }
+
+    /**
      * Flattens the map and all sub {@link SensorStats} objects. Keys will be flattened using
      * {@value #DELIMITER}. For example, if a sub {@link SensorStats} is added with key
      * {@code "key1"} containing the key value pair {@code ("key2", "value")}, the flattened map
@@ -91,11 +120,31 @@
         Collections.sort(keys);
         for (String key : keys) {
             Object value = flattened.get(key);
-            if (value instanceof Double || value instanceof Float) {
-                Log.v(tag, String.format("%s: %.4f", key, value));
+            if (value instanceof boolean[]) {
+                logStat(tag, key, Arrays.toString((boolean[]) value));
+            } else if (value instanceof byte[]) {
+                logStat(tag, key, Arrays.toString((byte[]) value));
+            } else if (value instanceof char[]) {
+                logStat(tag, key, Arrays.toString((char[]) value));
+            } else if (value instanceof double[]) {
+                logStat(tag, key, Arrays.toString((double[]) value));
+            } else if (value instanceof float[]) {
+                logStat(tag, key, Arrays.toString((float[]) value));
+            } else if (value instanceof int[]) {
+                logStat(tag, key, Arrays.toString((int[]) value));
+            } else if (value instanceof long[]) {
+                logStat(tag, key, Arrays.toString((long[]) value));
+            } else if (value instanceof short[]) {
+                logStat(tag, key, Arrays.toString((short[]) value));
+            } else if (value instanceof Object[]) {
+                logStat(tag, key, Arrays.toString((Object[]) value));
             } else {
-                Log.v(tag, String.format("%s: %s", key, value.toString()));
+                logStat(tag, key, value.toString());
             }
         }
     }
+
+    private static void logStat(String tag, String key, String value) {
+        Log.v(tag, String.format("%s: %s", key, value));
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelper.java
deleted file mode 100644
index 76e243f..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelper.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.cts.helpers;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Set of static helper methods to verify sensor CTS tests.
- */
-public class SensorVerificationHelper {
-
-    public static final String EVENT_ORDER_COUNT_KEY = "event_order_count";
-    public static final String EVENT_ORDER_POSITIONS_KEY = "event_order_positions";
-    public static final String FREQUENCY_KEY = "frequency";
-    public static final String JITTER_95_PERCENTILE_KEY = "jitter_95_percentile";
-    public static final String MEAN_KEY = "mean";
-    public static final String MAGNITUDE_KEY = "magnitude";
-    public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
-
-    private static final int MESSAGE_LENGTH = 3;
-
-    /**
-     * Class which holds results from the verification.
-     */
-    public static class VerificationResult {
-        private boolean mFailed = false;
-        private String mMessage = null;
-        private Map<String, Object> mValueMap = new HashMap<String, Object>();
-
-        public void fail(String messageFormat, Object ... args) {
-            mFailed = true;
-            mMessage = String.format(messageFormat, args);
-        }
-
-        public boolean isFailed() {
-            return mFailed;
-        }
-
-        public String getFailureMessage() {
-            return mMessage;
-        }
-
-        public void putValue(String key, Object value) {
-            mValueMap.put(key, value);
-        }
-
-        public Set<String> getKeys() {
-            return mValueMap.keySet();
-        }
-
-        public Object getValue(String key) {
-            return mValueMap.get(key);
-        }
-    }
-
-    /**
-     * Private constructor for static class.
-     */
-    private SensorVerificationHelper() {}
-
-    /**
-     * Verify that the events are in the correct order.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @return a {@link VerificationResult} containing the verification info including the keys
-     *     "count" which is the number of events out of order and "positions" which contains an
-     *     array of indexes that were out of order.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyEventOrdering(TestSensorEvent[] events) {
-        VerificationResult result = new VerificationResult();
-        List<Integer> indices = new ArrayList<Integer>();
-        long maxTimestamp = events[0].timestamp;
-        for (int i = 1; i < events.length; i++) {
-            long currentTimestamp = events[i].timestamp;
-            if (currentTimestamp < maxTimestamp) {
-                indices.add(i);
-            } else if (currentTimestamp > maxTimestamp) {
-                maxTimestamp = currentTimestamp;
-            }
-        }
-
-        result.putValue(EVENT_ORDER_COUNT_KEY, indices.size());
-        result.putValue(EVENT_ORDER_POSITIONS_KEY, indices);
-
-        if (indices.size() > 0) {
-            StringBuilder sb = new StringBuilder();
-            sb.append(indices.size()).append(" events out of order: ");
-            for (int i = 0; i < Math.min(indices.size(), MESSAGE_LENGTH); i++) {
-                int index = indices.get(i);
-                sb.append(String.format("position=%d, previous=%d, timestamp=%d; ", index,
-                        events[index - 1].timestamp, events[index].timestamp));
-            }
-            if (indices.size() > MESSAGE_LENGTH) {
-                sb.append(indices.size() - MESSAGE_LENGTH).append(" more");
-            } else {
-                // Delete the "; "
-                sb.delete(sb.length() - 2, sb.length());
-            }
-
-            result.fail(sb.toString());
-        }
-
-        return result;
-    }
-
-    /**
-     * Verify that the sensor frequency matches the expected frequency.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param expected The expected frequency in Hz
-     * @param lowerThreshold The acceptable margin of error in Hz for the lower bound
-     * @param upperThreshold The acceptable margin of error in Hz for the upper bound
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "frequency" which is the computed frequency of the events in Hz.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyFrequency(TestSensorEvent[] events, double expected,
-            double lowerThreshold, double upperThreshold) {
-        VerificationResult result = new VerificationResult();
-        List<Long> timestampDelayValues = SensorCtsHelper.getTimestampDelayValues(events);
-        double frequency = SensorCtsHelper.getFrequency(
-                SensorCtsHelper.getMean(timestampDelayValues), TimeUnit.NANOSECONDS);
-        result.putValue(FREQUENCY_KEY, frequency);
-
-        if (frequency <= expected - lowerThreshold || frequency >= expected + upperThreshold) {
-            result.fail("Frequency out of range: frequency=%.2fHz, "
-                    + "expected=(%.2f-%.2fHz, %.2f+.%2f)", frequency, expected, lowerThreshold,
-                    expected, upperThreshold);
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the jitter is in an acceptable range
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param expected the expected period in ns
-     * @param threshold The acceptable margin of error as a percentage
-     * @return a {@link VerificationResult} containing the verification info including the keys
-     *     "jitter" which is the list of computed jitter values and "jitter95Percentile" which is
-     *     95th percentile of the jitter values.
-     * @throws IllegalStateException if number of events less than 2.
-     */
-    public static VerificationResult verifyJitter(TestSensorEvent[] events, int expected,
-            int threshold) {
-        VerificationResult result = new VerificationResult();
-        List<Double> jitterValues = SensorCtsHelper.getJitterValues(events);
-        double jitter95Percentile = SensorCtsHelper.get95PercentileValue(jitterValues);
-        result.putValue(JITTER_95_PERCENTILE_KEY, jitter95Percentile);
-
-        if (jitter95Percentile > expected * (threshold / 100.0)) {
-            result.fail("Jitter out of range: jitter at 95th percentile=%.0fns, expected=<%.0fns",
-                    jitter95Percentile, expected * (threshold / 100.0));
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the means matches the expected measurement.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param expected The array of expected values
-     * @param threshold The array of thresholds
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "mean" which is the computed means for each value of the sensor.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyMean(TestSensorEvent[] events, float[] expected,
-            float[] threshold) {
-        VerificationResult result = new VerificationResult();
-        Float[] means = SensorCtsHelper.getMeans(events);
-        result.putValue(MEAN_KEY, Arrays.asList(means));
-
-        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] - expected[i]) > threshold[i]) {
-                failed = true;
-            }
-            meanSb.append(String.format("%.2f", means[i]));
-            if (i != means.length - 1) meanSb.append(", ");
-            expectedSb.append(String.format("%.2f+/-%.2f", expected[i], threshold[i]));
-            if (i != means.length - 1) expectedSb.append(", ");
-        }
-        if (means.length > 1) {
-            meanSb.append(")");
-            expectedSb.append(")");
-        }
-
-        if (failed) {
-            result.fail("Mean out of range: mean=%s, expected=%s",
-                    meanSb.toString(), expectedSb.toString());
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the mean of the magnitude of the sensors vector is within the expected range.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param expected The expected value
-     * @param threshold The threshold
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "magnitude" which is the mean of the computed magnitude of the sensor values.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyMagnitude(TestSensorEvent[] events, float expected,
-            float threshold) {
-        VerificationResult result = new VerificationResult();
-        Collection<Float> magnitudes = new ArrayList<Float>(events.length);
-
-        for (TestSensorEvent event : events) {
-            float sumOfSquares = 0;
-            for (int i = 0; i < event.values.length; i++) {
-                sumOfSquares += event.values[i] * event.values[i];
-            }
-            magnitudes.add((float) Math.sqrt(sumOfSquares));
-        }
-
-        float mean = (float) SensorCtsHelper.getMean(magnitudes);
-        result.putValue(MAGNITUDE_KEY, mean);
-
-        if (Math.abs(mean - expected) > threshold) {
-            result.fail(String.format("Magnitude mean out of range: mean=%s, expected=%s+/-%s",
-                    mean, expected, threshold));
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the sign of each of the sensor values is correct.
-     * <p>
-     * If the value of the measurement is in [-threshold, threshold], the sign is considered 0. If
-     * it is less than -threshold, it is considered -1. If it is greater than threshold, it is
-     * considered 1.
-     * </p>
-     *
-     * @param events
-     * @param threshold The threshold that needs to be crossed to consider a measurement nonzero
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "mean" which is the computed means for each value of the sensor.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifySignum(TestSensorEvent[] events, int[] expected,
-            float[] threshold) {
-        VerificationResult result = new VerificationResult();
-        for (int i = 0; i < expected.length; i++) {
-            if (!(expected[i] == -1 || expected[i] == 0 || expected[i] == 1)) {
-                throw new IllegalArgumentException("Expected value must be -1, 0, or 1");
-            }
-        }
-        Float[] means = SensorCtsHelper.getMeans(events);
-        result.putValue(MEAN_KEY, Arrays.asList(means));
-
-        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++) {
-            meanSb.append(String.format("%.2f", means[i]));
-            if (i != means.length - 1) meanSb.append(", ");
-
-            if (expected[i] == 0) {
-                if (Math.abs(means[i]) > threshold[i]) {
-                    failed = true;
-                }
-                expectedSb.append(String.format("[%.2f, %.2f]", -threshold[i], threshold[i]));
-            } else {
-                if (expected[i] > 0) {
-                    if (means[i] <= threshold[i]) {
-                        failed = true;
-                    }
-                    expectedSb.append(String.format("(%.2f, inf)", threshold[i]));
-                } else {
-                    if (means[i] >= -1 * threshold[i]) {
-                        failed = true;
-                    }
-                    expectedSb.append(String.format("(-inf, %.2f)", -1 * threshold[i]));
-                }
-            }
-            if (i != means.length - 1) expectedSb.append(", ");
-        }
-        if (means.length > 1) {
-            meanSb.append(")");
-            expectedSb.append(")");
-        }
-
-        if (failed) {
-            result.fail("Signum out of range: mean=%s, expected=%s",
-                    meanSb.toString(), expectedSb.toString());
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the standard deviations is within the expected range.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param threshold The array of thresholds
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "stddevs" which is the computed standard deviations for each value of the sensor.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyStandardDeviation(TestSensorEvent[] events,
-            float[] threshold) {
-        VerificationResult result = new VerificationResult();
-        Float[] standardDeviations = SensorCtsHelper.getStandardDeviations(events);
-        result.putValue(STANDARD_DEVIATION_KEY, Arrays.asList(standardDeviations));
-
-        boolean failed = false;
-        StringBuilder stddevSb = new StringBuilder();
-        StringBuilder expectedSb = new StringBuilder();
-
-        if (standardDeviations.length > 1) {
-            stddevSb.append("(");
-            expectedSb.append("(");
-        }
-        for (int i = 0; i < standardDeviations.length; i++) {
-            if (standardDeviations[i] > threshold[i]) {
-                failed = true;
-            }
-            stddevSb.append(String.format("%.2f", standardDeviations[i]));
-            if (i != standardDeviations.length - 1) stddevSb.append(", ");
-            expectedSb.append(String.format("<%.2f", threshold[i]));
-            if (i != standardDeviations.length - 1) expectedSb.append(", ");
-        }
-        if (standardDeviations.length > 1) {
-            stddevSb.append(")");
-            expectedSb.append(")");
-        }
-
-        if (failed) {
-            result.fail("Standard deviation out of range: stddev=%s, expected=%s",
-                    stddevSb.toString(), expectedSb.toString());
-        }
-        return result;
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelperTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelperTest.java
deleted file mode 100644
index fd3c03c..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelperTest.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers;
-
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-
-import junit.framework.TestCase;
-
-import java.util.List;
-
-/**
- * Unit tests for the {@link SensorVerificationHelper} class.
- */
-public class SensorVerificationHelperTest extends TestCase {
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyEventOrdering(TestSensorEvent[])}.
-     */
-    @SuppressWarnings("unchecked")
-    public void testVerifyEventOrdering() {
-        float[] values = {0, 1, 2, 3, 4};
-
-        long[] timestamps1 = {0, 0, 0, 0, 0};
-        TestSensorEvent[] events1 = getSensorEvents(timestamps1, values);
-        VerificationResult result = SensorVerificationHelper.verifyEventOrdering(events1);
-        assertFalse(result.isFailed());
-        assertEquals(0, result.getValue(SensorVerificationHelper.EVENT_ORDER_COUNT_KEY));
-
-        long[] timestamps2 = {0, 1, 2, 3, 4};
-        TestSensorEvent[] events2 = getSensorEvents(timestamps2, values);
-        result = SensorVerificationHelper.verifyEventOrdering(events2);
-        assertFalse(result.isFailed());
-        assertEquals(0, result.getValue(SensorVerificationHelper.EVENT_ORDER_COUNT_KEY));
-
-        long[] timestamps3 = {0, 2, 1, 3, 4};
-        TestSensorEvent[] events3 = getSensorEvents(timestamps3, values);
-        result = SensorVerificationHelper.verifyEventOrdering(events3);
-        assertTrue(result.isFailed());
-        assertEquals(1, result.getValue(SensorVerificationHelper.EVENT_ORDER_COUNT_KEY));
-        List<Integer> indices = (List<Integer>) result.getValue(
-                SensorVerificationHelper.EVENT_ORDER_POSITIONS_KEY);
-        assertTrue(indices.contains(2));
-
-        long[] timestamps4 = {4, 0, 1, 2, 3};
-        TestSensorEvent[] events4 = getSensorEvents(timestamps4, values);
-        result = SensorVerificationHelper.verifyEventOrdering(events4);
-        assertTrue(result.isFailed());
-        assertEquals(4, result.getValue(SensorVerificationHelper.EVENT_ORDER_COUNT_KEY));
-        indices = (List<Integer>) result.getValue(
-                SensorVerificationHelper.EVENT_ORDER_POSITIONS_KEY);
-        assertTrue(indices.contains(1));
-        assertTrue(indices.contains(2));
-        assertTrue(indices.contains(3));
-        assertTrue(indices.contains(4));
-    }
-
-    /**
-     * Test
-     * {@link SensorVerificationHelper#verifyFrequency(TestSensorEvent[], double, double, double)}.
-     */
-    public void testVerifyFrequency() {
-        float[] values = {0, 1, 2, 3, 4};
-        long[] timestamps = {0, 1000000, 2000000, 3000000, 4000000};  // 1000Hz
-        TestSensorEvent[] events = getSensorEvents(timestamps, values);
-
-        VerificationResult result = SensorVerificationHelper.verifyFrequency(events, 1000.0, 1.0,
-                1.0);
-        assertFalse(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-
-        result = SensorVerificationHelper.verifyFrequency(events, 950.0, 100.0, 100.0);
-        assertFalse(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-
-        result = SensorVerificationHelper.verifyFrequency(events, 1050.0, 100.0, 100.0);
-        assertFalse(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-
-        result = SensorVerificationHelper.verifyFrequency(events, 950.0, 100.0, 25.0);
-        assertTrue(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-
-        result = SensorVerificationHelper.verifyFrequency(events, 1050.0, 25.0, 100.0);
-        assertTrue(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyJitter(TestSensorEvent[], int, int)}.
-     */
-    public void testVerifyJitter() {
-        final int SAMPLE_SIZE = 100;
-        float[] values = new float[SAMPLE_SIZE];
-        for (int i = 0; i < SAMPLE_SIZE; i++) {
-            values[i] = i;
-        }
-
-        long[] timestamps1 = new long[SAMPLE_SIZE];  // 100 samples at 1000Hz
-        for (int i = 0; i < SAMPLE_SIZE; i++) {
-            timestamps1[i] = i * 100000;
-        }
-        TestSensorEvent[] events1 = getSensorEvents(timestamps1, values);
-        VerificationResult result = SensorVerificationHelper.verifyJitter(events1, 1000, 10);
-        assertFalse(result.isFailed());
-        Double jitter95 = (Double) result.getValue(
-                SensorVerificationHelper.JITTER_95_PERCENTILE_KEY);
-        assertEquals(0.0, jitter95, 0.01);
-
-        long[] timestamps2 = new long[SAMPLE_SIZE];  // 90 samples at 1000Hz, 10 samples at 2000Hz
-        long timestamp = 0;
-        for (int i = 0; i < SAMPLE_SIZE; i++) {
-            timestamps2[i] = timestamp;
-            timestamp += (i % 10 == 0) ? 500000 : 1000000;
-        }
-        TestSensorEvent[] events2 = getSensorEvents(timestamps2, values);
-        result = SensorVerificationHelper.verifyJitter(events2, 1000, 10);
-        assertTrue(result.isFailed());
-        assertNotNull(result.getValue(SensorVerificationHelper.JITTER_95_PERCENTILE_KEY));
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyMean(TestSensorEvent[], float[], float[])}.
-     */
-    public void testVerifyMean() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-        float[] values1 = {0, 1, 2, 3, 4};
-        float[] values2 = {1, 2, 3, 4, 5};
-        float[] values3 = {0, 1, 4, 9, 16};
-        TestSensorEvent[] events = getSensorEvents(timestamps, values1, values2, values3);
-
-        float[] expected1 = {2.0f, 3.0f, 6.0f};
-        float[] threshold1 = {0.1f, 0.1f, 0.1f};
-        VerificationResult result = SensorVerificationHelper.verifyMean(events, expected1,
-                threshold1);
-        assertFalse(result.isFailed());
-        @SuppressWarnings("unchecked")
-        List<Float> means = (List<Float>) result.getValue(SensorVerificationHelper.MEAN_KEY);
-        assertEquals(2.0f, means.get(0), 0.01);
-        assertEquals(3.0f, means.get(1), 0.01);
-        assertEquals(6.0f, means.get(2), 0.01);
-
-        float[] expected = {2.5f, 2.5f, 5.5f};
-        float[] threshold = {0.6f, 0.6f, 0.6f};
-        result = SensorVerificationHelper.verifyMean(events, expected, threshold);
-        assertFalse(result.isFailed());
-
-        expected = new float[]{2.5f, 2.5f, 5.5f};
-        threshold = new float[]{0.1f, 0.6f, 0.6f};
-        result = SensorVerificationHelper.verifyMean(events, expected, threshold);
-        assertTrue(result.isFailed());
-
-        expected = new float[]{2.5f, 2.5f, 5.5f};
-        threshold = new float[]{0.6f, 0.1f, 0.6f};
-        result = SensorVerificationHelper.verifyMean(events, expected, threshold);
-        assertTrue(result.isFailed());
-
-        threshold = new float[]{2.5f, 2.5f, 5.5f};
-        threshold = new float[]{0.6f, 0.6f, 0.1f};
-        result = SensorVerificationHelper.verifyMean(events, expected, threshold);
-        assertTrue(result.isFailed());
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyMagnitude(TestSensorEvent[], float, float)}.
-     */
-    public void testVerifyMagnitude() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-        float[] values1 = {0, 4, 3, 0, 6};
-        float[] values2 = {3, 0, 4, 0, 0};
-        float[] values3 = {4, 3, 0, 4, 0};
-        TestSensorEvent[] events = getSensorEvents(timestamps, values1, values2, values3);
-
-        float expected = 5.0f;
-        float threshold = 0.1f;
-        VerificationResult result = SensorVerificationHelper.verifyMagnitude(events, expected,
-                threshold);
-        assertFalse(result.isFailed());
-        assertEquals(5.0f, (Float) result.getValue(SensorVerificationHelper.MAGNITUDE_KEY), 0.01);
-
-        expected = 4.5f;
-        threshold = 0.6f;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertFalse(result.isFailed());
-
-        expected = 5.5f;
-        threshold = 0.6f;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertFalse(result.isFailed());
-
-        expected = 4.5f;
-        threshold = 0.1f;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertTrue(result.isFailed());
-
-        expected = 5.5f;
-        threshold = 0.1f;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertTrue(result.isFailed());
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifySignum(TestSensorEvent[], int[], float[])}.
-     */
-    public void testVerifySignum() {
-        long[] timestamps = {0};
-        float[][] values = {{1}, {0.2f}, {0}, {-0.2f}, {-1}};
-        TestSensorEvent[] events = getSensorEvents(timestamps, values);
-
-        int[] expected = {1, 1, 0, -1, -1};
-        float[] threshold = {0.1f, 0.1f, 0.1f, 0.1f, 0.1f};
-        VerificationResult result = SensorVerificationHelper.verifySignum(events, expected,
-                threshold);
-        assertFalse(result.isFailed());
-        assertNotNull(result.getValue(SensorVerificationHelper.MEAN_KEY));
-
-        expected = new int[]{1, 0, 0, 0, -1};
-        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
-        result = SensorVerificationHelper.verifySignum(events, expected, threshold);
-        assertFalse(result.isFailed());
-
-        expected = new int[]{0, 1, 0, -1, 0};
-        threshold = new float[]{1.5f, 0.1f, 0.1f, 0.1f, 1.5f};
-        result = SensorVerificationHelper.verifySignum(events, expected, threshold);
-        assertFalse(result.isFailed());
-
-        expected = new int[]{1, 0, 0, 0, 1};
-        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
-        result = SensorVerificationHelper.verifySignum(events, expected, threshold);
-        assertTrue(result.isFailed());
-
-        expected = new int[]{-1, 0, 0, 0, -1};
-        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
-        result = SensorVerificationHelper.verifySignum(events, expected, threshold);
-        assertTrue(result.isFailed());
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyStandardDeviation(TestSensorEvent[], float[])}.
-     */
-    public void testVerifyStandardDeviation() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-        float[] values1 = {0, 1, 2, 3, 4};  // sqrt(2.5)
-        float[] values2 = {1, 2, 3, 4, 5};  // sqrt(2.5)
-        float[] values3 = {0, 2, 4, 6, 8};  // sqrt(10.0)
-        TestSensorEvent[] events = getSensorEvents(timestamps, values1, values2, values3);
-
-        float[] threshold = {2, 2, 4};
-        VerificationResult result = SensorVerificationHelper.verifyStandardDeviation(events,
-                threshold);
-        assertFalse(result.isFailed());
-        @SuppressWarnings("unchecked")
-        List<Float> stddevs = (List<Float>) result.getValue(
-                SensorVerificationHelper.STANDARD_DEVIATION_KEY);
-        assertEquals(Math.sqrt(2.5), stddevs.get(0), 0.01);
-        assertEquals(Math.sqrt(2.5), stddevs.get(1), 0.01);
-        assertEquals(Math.sqrt(10.0), stddevs.get(2), 0.01);
-
-        threshold = new float[]{1, 2, 4};
-        result = SensorVerificationHelper.verifyStandardDeviation(events, threshold);
-        assertTrue(result.isFailed());
-
-        threshold = new float[]{2, 1, 4};
-        result = SensorVerificationHelper.verifyStandardDeviation(events, threshold);
-        assertTrue(result.isFailed());
-
-        threshold = new float[]{2, 2, 3};
-        result = SensorVerificationHelper.verifyStandardDeviation(events, threshold);
-        assertTrue(result.isFailed());
-    }
-
-    private TestSensorEvent[] getSensorEvents(long[] timestamps, float[] ... values) {
-        TestSensorEvent[] events = new TestSensorEvent[timestamps.length];
-        for (int i = 0; i < timestamps.length; i++) {
-            float[] eventValues = new float[values.length];
-            for (int j = 0; j < values.length; j++) {
-                eventValues[j] = values[j][i];
-            }
-            events[i] = new TestSensorEvent(null, timestamps[i], 0, eventValues);
-        }
-        return events;
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
index c22ad8e..b349e1b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
@@ -51,14 +51,14 @@
     /**
      * Constructor for TestSensorEvent. Exposed for unit testing.
      */
-    TestSensorEvent(Sensor sensor, long timestamp, int accuracy, float[] values) {
+    public TestSensorEvent(Sensor sensor, long timestamp, int accuracy, float[] values) {
         this(sensor, timestamp, timestamp, accuracy, values);
     }
 
     /**
      * Constructor for TestSensorEvent. Exposed for unit testing.
      */
-    TestSensorEvent(Sensor sensor, long timestamp, long receivedTimestamp, int accuracy,
+    public TestSensorEvent(Sensor sensor, long timestamp, long receivedTimestamp, int accuracy,
             float[] values) {
         this.sensor = sensor;
         this.timestamp = timestamp;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
new file mode 100644
index 0000000..2d13ce9
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link SensorEventListener2} which performs operations such as waiting for a specific number of
+ * events or for a specific time, or waiting for a flush to complete. This class performs
+ * verifications and will throw {@link AssertionError}s if there are any errors. It may also wrap
+ * another {@link SensorEventListener2}.
+ */
+public class TestSensorEventListener implements SensorEventListener2 {
+    public static final String LOG_TAG = "TestSensorEventListener";
+    public static final boolean DEBUG = true;
+    private static final long EVENT_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
+    private static final long FLUSH_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
+
+    private final SensorEventListener2 mListener;
+
+    private volatile CountDownLatch mEventLatch = null;
+    private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
+
+    private Sensor mSensor = null;
+    private int mRateUs = 0;
+    private int mMaxBatchReportLatencyUs = 0;
+
+    /**
+     * Construct a {@link TestSensorEventListener}.
+     */
+    public TestSensorEventListener() {
+        this(null);
+    }
+
+    /**
+     * Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}.
+     */
+    public TestSensorEventListener(SensorEventListener2 listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Set the sensor, rate, and batch report latency used for the assertions.
+     */
+    public void setSensorInfo(Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
+        mSensor = sensor;
+        mRateUs = rateUs;
+        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if(mEventLatch != null) {
+            mEventLatch.countDown();
+        }
+        if (mListener != null) {
+            mListener.onSensorChanged(event);
+        }
+        if (DEBUG) {
+            StringBuilder valuesSb = new StringBuilder();
+            if (event.values.length == 1) {
+                valuesSb.append(String.format("%.2f", event.values[0]));
+            } else {
+                valuesSb.append("[").append(String.format("%.2f", event.values[0]));
+                for (int i = 1; i < event.values.length; i++) {
+                    valuesSb.append(String.format(", %.2f", event.values[i]));
+                }
+                valuesSb.append("]");
+            }
+
+            Log.v(LOG_TAG, String.format(
+                    "Sensor %d: device_timestamp=%d, values=%s",
+                    mSensor.getType(), event.timestamp, Arrays.toString(event.values)));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        if (mListener != null) {
+            mListener.onAccuracyChanged(sensor, accuracy);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFlushCompleted(Sensor sensor) {
+        CountDownLatch latch = mFlushLatch;
+        mFlushLatch = new CountDownLatch(1);
+        if(latch != null) {
+            latch.countDown();
+        }
+        if (mListener != null) {
+            mListener.onFlushCompleted(sensor);
+        }
+    }
+
+    /**
+     * Wait for {@link #onFlushCompleted(Sensor)} to be called.
+     *
+     * @throws AssertionError if there was a timeout after {@value #FLUSH_TIMEOUT_US} &micro;s
+     */
+    public void waitForFlushComplete() {
+        CountDownLatch latch = mFlushLatch;
+        try {
+            if(latch != null) {
+                String message = SensorCtsHelper.formatAssertionMessage(mSensor, "WaitForFlush");
+                Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+            }
+        } catch(InterruptedException e) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Collect a specific number of {@link TestSensorEvent}s.
+     *
+     * @throws AssertionError if there was a timeout after {@value #FLUSH_TIMEOUT_US} &micro;s
+     */
+    public void waitForEvents(int eventCount) {
+        mEventLatch = new CountDownLatch(eventCount);
+        try {
+            int rateUs = SensorCtsHelper.getDelay(mSensor, mRateUs);
+            // Timeout is 2 * event count * expected period + batch timeout + default wait
+            long timeoutUs = ((2 * eventCount * rateUs)
+                    + mMaxBatchReportLatencyUs + EVENT_TIMEOUT_US);
+
+            String message = SensorCtsHelper.formatAssertionMessage(mSensor, "WaitForEvents",
+                    "count:%d, available:%d", eventCount, mEventLatch.getCount());
+            Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
+        } catch(InterruptedException e) {
+            // Ignore
+        } finally {
+            mEventLatch = null;
+        }
+    }
+
+    /**
+     * Collect {@link TestSensorEvent} for a specific duration.
+     */
+    public void waitForEvents(long duration, TimeUnit timeUnit) {
+        SensorCtsHelper.sleep(duration, timeUnit);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
index eb5c300..32bd3c9 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
@@ -26,8 +25,6 @@
 
 import junit.framework.Assert;
 
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -36,61 +33,41 @@
  * operations such as flushing the sensor events and gathering events. This class also manages
  * performing the test verifications for the sensor manager.
  * <p>
- * The class makes use of an internal {@link SensorEventListener2} in order to gather events and
- * check to make sure that flushes completed. An additionaly {@link SensorEventListener2} may be
- * provided in order to perform more complex tests.
- * </p><p>
  * This class requires that operations are performed in the following order:
  * <p><ul>
- * <li>{@link #registerListener()}</li>
- * <li>{@link #getEvents()}, {@link #getEvents(int)}, {@link #getEvents(long, TimeUnit)},
- * {@link #clearEvents()}, {@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
+ * <li>{@link #registerListener(TestSensorEventListener)}</li>
+ * <li>{@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
  * <li>{@link #unregisterListener()}</li>
  * </ul><p>Or:</p><ul>
- * <li>{@link #collectEvents(int)}</li>
+ * <li>{@link #runSensor(TestSensorEventListener, int)}</li>
  * </ul><p>Or:</p><ul>
- * <li>{@link #collectEvents(long, TimeUnit)}</li>
+ * <li>{@link #runSensor(TestSensorEventListener, long, TimeUnit)}</li>
  * </ul><p>
  * If methods are called outside of this order, they will print a warning to the log and then
- * return. Both {@link #collectEvents(int)} and {@link #collectEvents(long, TimeUnit)} will perform
- * the appropriate clean up and tear down.
+ * return. Both {@link #runSensor(TestSensorEventListener, int)}} and
+ * {@link #runSensor(TestSensorEventListener, long, TimeUnit)} will perform the appropriate
+ * set up and tear down.
  * <p>
  */
 public class TestSensorManager {
     private static final String LOG_TAG = "TestSensorManager";
-    private static final long EVENT_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
-    private static final long FLUSH_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
 
     private final SensorManager mSensorManager;
     private final Sensor mSensor;
     private final int mRateUs;
     private final int mMaxBatchReportLatencyUs;
-    private final SensorEventListener2 mSensorEventListener;
 
-    private TestSensorListener mTestSensorEventListener = null;
+    private TestSensorEventListener mTestSensorEventListener = null;
 
     /**
-     * Create a {@link TestSensorManager} with a {@link SensorEventListener2}. This can be used for
-     * tests which require special behavior to be triggered on methods such as
-     * {@link SensorEventListener2#onAccuracyChanged(Sensor, int)},
-     * {@link SensorEventListener2#onFlushCompleted(Sensor)}, or
-     * {@link SensorEventListener2#onSensorChanged(SensorEvent)}.
+     * Construct a {@link TestSensorManager}.
      */
     public TestSensorManager(Context context, int sensorType, int rateUs,
-            int maxBatchReportLatencyUs, SensorEventListener2 sensorEventListener) {
+            int maxBatchReportLatencyUs) {
         mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
         mSensor = SensorCtsHelper.getSensor(context, sensorType);
         mRateUs = rateUs;
         mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
-        mSensorEventListener = sensorEventListener;
-    }
-
-    /**
-     * Create a {@link TestSensorManager} without a {@link SensorEventListener2}.
-     */
-    public TestSensorManager(Context context, int sensorType, int rateUs,
-            int maxBatchReportLatencyUs) {
-        this(context, sensorType, rateUs, maxBatchReportLatencyUs, null);
     }
 
     /**
@@ -99,15 +76,16 @@
      * @throws AssertionError if there was an error registering the listener with the
      * {@link SensorManager}
      */
-    public void registerListener() {
+    public void registerListener(TestSensorEventListener listener) {
         if (mTestSensorEventListener != null) {
             Log.w(LOG_TAG, "Listener already registered, returning.");
             return;
         }
 
-        mTestSensorEventListener = new TestSensorListener(mSensorEventListener);
+        mTestSensorEventListener = listener != null ? listener : new TestSensorEventListener();
+        mTestSensorEventListener.setSensorInfo(mSensor, mRateUs, mMaxBatchReportLatencyUs);
 
-        String message = formatAssertionMessage("registerListener");
+        String message = SensorCtsHelper.formatAssertionMessage(mSensor, "registerListener");
         boolean result = mSensorManager.registerListener(mTestSensorEventListener, mSensor, mRateUs,
                 mMaxBatchReportLatencyUs);
         Assert.assertTrue(message, result);
@@ -127,64 +105,27 @@
     }
 
     /**
-     * Get a specific number of {@link TestSensorEvent}s and then clear the event queue. This method
-     * will perform a no-op if the sensor is not registered.
-     *
-     * @throws AssertionError if there is a time out while collecting events
+     * Wait for a specific number of events.
      */
-    public TestSensorEvent[] getEvents(int count) {
-        if (mTestSensorEventListener == null) {
-            Log.w(LOG_TAG, "No listener registered, returning.");
-            return null;
-        }
-
-        mTestSensorEventListener.waitForEvents(count);
-        TestSensorEvent[] events = mTestSensorEventListener.getEvents();
-        mTestSensorEventListener.clearEvents();
-
-        return events;
-    }
-
-    /**
-     * Get the {@link TestSensorEvent} for a specific duration and then clear the event queue. This
-     * method will perform a no-op if the sensor is not registered.
-     */
-    public TestSensorEvent[] getEvents(long duration, TimeUnit timeUnit) {
-        if (mTestSensorEventListener == null) {
-            Log.w(LOG_TAG, "No listener registered, returning.");
-            return null;
-        }
-
-        mTestSensorEventListener.waitForEvents(duration, timeUnit);
-        TestSensorEvent[] events = mTestSensorEventListener.getEvents();
-        mTestSensorEventListener.clearEvents();
-
-        return events;
-    }
-
-    /**
-     * Get the {@link TestSensorEvent} from the event queue. This method will perform a no-op if the
-     * sensor is not registered.
-     */
-    public TestSensorEvent[] getEvents() {
-        if (mTestSensorEventListener == null) {
-            Log.w(LOG_TAG, "No listener registered, returning.");
-            return null;
-        }
-
-        return mTestSensorEventListener.getEvents();
-    }
-
-    /**
-     * Clear the event queue. This method will perform a no-op if the sensor is not registered.
-     */
-    public void clearEvents() {
+    public void waitForEvents(int eventCount) {
         if (mTestSensorEventListener == null) {
             Log.w(LOG_TAG, "No listener registered, returning.");
             return;
         }
 
-        mTestSensorEventListener.clearEvents();
+        mTestSensorEventListener.waitForEvents(eventCount);
+    }
+
+    /**
+     * Wait for a specific duration.
+     */
+    public void waitForEvents(long duration, TimeUnit timeUnit) {
+        if (mTestSensorEventListener == null) {
+            Log.w(LOG_TAG, "No listener registered, returning.");
+            return;
+        }
+
+        mTestSensorEventListener.waitForEvents(duration, timeUnit);
     }
 
     /**
@@ -198,7 +139,7 @@
             return;
         }
 
-        String message = formatAssertionMessage("Flush");
+        String message = SensorCtsHelper.formatAssertionMessage(mSensor, "Flush");
         Assert.assertTrue(message, mSensorManager.flush(mTestSensorEventListener));
     }
 
@@ -236,43 +177,34 @@
     }
 
     /**
-     * Collect a specific number of {@link TestSensorEvent}s. This method registers the event
-     * listener before collecting the events and then unregisters the listener after. It will
-     * perform a no-op if the sensor is already registered.
-     *
-     * @throws AssertionError if there is are errors registering the event listener or if there is
-     * a time out collecting the events
+     * Register a listener, wait for a specific number of events, and then unregister the listener.
      */
-    public TestSensorEvent[] collectEvents(int eventCount) {
+    public void runSensor(TestSensorEventListener listener, int eventCount) {
         if (mTestSensorEventListener != null) {
             Log.w(LOG_TAG, "Listener already registered, returning.");
-            return null;
+            return;
         }
 
         try {
-            registerListener();
-            return getEvents(eventCount);
+            registerListener(listener);
+            waitForEvents(eventCount);
         } finally {
             unregisterListener();
         }
     }
 
     /**
-     * Collect the {@link TestSensorEvent} for a specific duration. This method registers the event
-     * listener before collecting the events and then unregisters the listener after. It will
-     * perform a no-op if the sensor is already registered.
-     *
-     * @throws AssertionError if there is are errors registering the event listener
+     * Register a listener, wait for a specific duration, and then unregister the listener.
      */
-    public TestSensorEvent[] collectEvents(long duration, TimeUnit timeUnit) {
+    public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit) {
         if (mTestSensorEventListener != null) {
             Log.w(LOG_TAG, "Listener already registered, returning.");
-            return null;
+            return;
         }
 
         try {
-            registerListener();
-            return getEvents(duration, timeUnit);
+            registerListener(listener);
+            waitForEvents(duration, timeUnit);
         } finally {
             unregisterListener();
         }
@@ -284,143 +216,4 @@
     public Sensor getSensor() {
         return mSensor;
     }
-
-    /**
-     * Helper class which collects events and ensures the flushes are completed in a timely manner.
-     */
-    private class TestSensorListener implements SensorEventListener2 {
-        private final SensorEventListener2 mListener;
-
-        private final ConcurrentLinkedDeque<TestSensorEvent> mSensorEventsList =
-                new ConcurrentLinkedDeque<TestSensorEvent>();
-
-        private volatile CountDownLatch mEventLatch = null;
-        private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
-
-        public TestSensorListener(SensorEventListener2 listener) {
-            mListener = listener;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            mSensorEventsList.addLast(new TestSensorEvent(event, System.nanoTime()));
-            if(mEventLatch != null) {
-                mEventLatch.countDown();
-            }
-            if (mListener != null) {
-                mListener.onSensorChanged(event);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            if (mListener != null) {
-                mListener.onAccuracyChanged(sensor, accuracy);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void onFlushCompleted(Sensor sensor) {
-            CountDownLatch latch = mFlushLatch;
-            mFlushLatch = new CountDownLatch(1);
-            if(latch != null) {
-                latch.countDown();
-            }
-            if (mListener != null) {
-                mListener.onFlushCompleted(sensor);
-            }
-        }
-
-        /**
-         * Wait for {@link #onFlushCompleted(Sensor)} to be called.
-         *
-         * @throws AssertionError if there was a timeout after {@value #FLUSH_TIMEOUT_US} &micro;s
-         * @throws InterruptedException if the thread was interrupted
-         */
-        public void waitForFlushComplete() throws InterruptedException {
-            CountDownLatch latch = mFlushLatch;
-            if(latch != null) {
-                String message = formatAssertionMessage("WaitForFlush");
-                Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
-            }
-        }
-
-        /**
-         * Collect a specific number of {@link TestSensorEvent}s.
-         *
-         * @throws AssertionError if there was a timeout after {@value #FLUSH_TIMEOUT_US} &micro;s
-         */
-        public void waitForEvents(int eventCount) {
-            mEventLatch = new CountDownLatch(eventCount);
-            clearEvents();
-            try {
-                int rateUs = SensorCtsHelper.getDelay(mSensor, mRateUs);
-                // Timeout is 2 * event count * expected period + default wait
-                long timeoutUs = (2 * eventCount * rateUs) + EVENT_TIMEOUT_US;
-
-                String message = formatAssertionMessage("WaitForEvents",
-                        "count:%d, available:%d", eventCount, mSensorEventsList.size());
-                Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
-            } catch(InterruptedException e) {
-                // Ignore
-            } finally {
-                mEventLatch = null;
-            }
-        }
-
-        /**
-         * Collect {@link TestSensorEvent} for a specific duration.
-         */
-        public void waitForEvents(long duration, TimeUnit timeUnit) {
-            clearEvents();
-            SensorCtsHelper.sleep(duration, timeUnit);
-        }
-
-        /**
-         * Get the {@link TestSensorEvent} from the event queue.
-         */
-        public TestSensorEvent[] getEvents() {
-            return mSensorEventsList.toArray(new TestSensorEvent[0]);
-        }
-
-        /**
-         * Clear the event queue.
-         */
-        public void clearEvents() {
-            mSensorEventsList.clear();
-        }
-    }
-
-    /**
-     * Format an assertion message.
-     *
-     * @param label The verification name
-     * @return The formatted string
-     */
-    private String formatAssertionMessage(String label) {
-        return formatAssertionMessage(label, "");
-    }
-
-    /**
-     * Format an assertion message with a custom message.
-     *
-     * @param label The verification name
-     * @param format The additional format string
-     * @param params The additional format params
-     * @return The formatted string
-     */
-    private String formatAssertionMessage(String label, String format, Object ... params) {
-        return String.format("%s | %s, handle: %d | %s",
-                SensorTestInformation.getSensorName(mSensor.getType()), mSensor.getHandle(),
-                String.format(format, params));
-    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
new file mode 100644
index 0000000..b8f67be
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+/**
+ * A {@link TestSensorEventListener} which performs validations on the received events on the fly.
+ * This class is useful for long running tests where it is not practical to store all the events to
+ * be processed after.
+ */
+public class ValidatingSensorEventListener extends TestSensorEventListener {
+
+    private final Collection<ISensorVerification> mVerifications =
+            new LinkedList<ISensorVerification>();
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener} with an additional
+     * {@link SensorEventListener2}.
+     */
+    public ValidatingSensorEventListener(SensorEventListener2 listener,
+            ISensorVerification ... verifications) {
+        super(listener);
+        for (ISensorVerification verification : verifications) {
+            mVerifications.add(verification);
+        }
+    }
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener} with an additional
+     * {@link SensorEventListener2}.
+     */
+    public ValidatingSensorEventListener(SensorEventListener2 listener,
+            Collection<ISensorVerification> verifications) {
+        this(listener, verifications.toArray(new ISensorVerification[0]));
+    }
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener}.
+     */
+    public ValidatingSensorEventListener(ISensorVerification ... verifications) {
+        this(null, verifications);
+    }
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener}.
+     */
+    public ValidatingSensorEventListener(Collection<ISensorVerification> verifications) {
+        this(null, verifications);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        TestSensorEvent testEvent = new TestSensorEvent(event, System.nanoTime());
+        for (ISensorVerification verification : mVerifications) {
+            verification.addSensorEvent(testEvent);
+        }
+        super.onSensorChanged(event);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
index 5f6e558..db1dff7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.hardware.cts.helpers.sensoroperations;
 
 import android.hardware.cts.helpers.SensorStats;
@@ -8,14 +24,7 @@
  */
 public abstract class AbstractSensorOperation implements ISensorOperation {
 
-    private final SensorStats mStats = new SensorStats();
-
-    /**
-     * Wrapper around {@link SensorStats#addValue(String, Object)}
-     */
-    protected void addValue(String key, Object value) {
-        mStats.addValue(key, value);
-    }
+    protected final SensorStats mStats = new SensorStats();
 
     /**
      * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
index af78db0..bf43189 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -26,8 +26,6 @@
  * {@link ISensorOperation}.
  */
 public class DelaySensorOperation implements ISensorOperation {
-    private static final int NANOS_PER_MILLI = 1000000;
-
     private final ISensorOperation mOperation;
     private final long mDelay;
     private final TimeUnit mTimeUnit;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
index 0c7e771..cbcbc0d 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -58,7 +58,7 @@
         long delayNs = TimeUnit.NANOSECONDS.convert(mDelay, mTimeUnit);
         try {
             Thread.sleep(delayNs / NANOS_PER_MILLI, (int) (delayNs % NANOS_PER_MILLI));
-            addValue("executed", new Boolean(true));
+            mStats.addValue("executed", true);
             if (mFail) {
                 Assert.fail("FakeSensorOperation failed");
             }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
new file mode 100644
index 0000000..b40176d
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.ValidatingSensorEventListener;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+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.StandardDeviationVerification;
+
+import junit.framework.Assert;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
+ * <p>
+ * Provides methods to set test expectations as well as providing a set of default expectations
+ * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
+ * events and then run all the tests.
+ * </p>
+ */
+public class TestSensorOperation extends AbstractSensorOperation {
+    private final TestSensorManager mSensorManager;
+    private final Context mContext;
+    private final int mSensorType;
+    private final int mRateUs;
+    private final int mMaxBatchReportLatencyUs;
+    private final Integer mEventCount;
+    private final Long mDuration;
+    private final TimeUnit mTimeUnit;
+
+    private final Collection<ISensorVerification> mVerifications =
+            new HashSet<ISensorVerification>();
+
+    /**
+     * Create a {@link TestSensorOperation}.
+     *
+     * @param context the {@link Context}.
+     * @param sensorType the sensor type
+     * @param rateUs the rate that
+     * @param maxBatchReportLatencyUs the max batch report latency
+     * @param eventCount the number of events to gather
+     */
+    public TestSensorOperation(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs, int eventCount) {
+        this(context, sensorType, rateUs, maxBatchReportLatencyUs, eventCount, null, null);
+    }
+
+    /**
+     * Create a {@link TestSensorOperation}.
+     *
+     * @param context the {@link Context}.
+     * @param sensorType the sensor type
+     * @param rateUs the rate that
+     * @param maxBatchReportLatencyUs the max batch report latency
+     * @param duration the duration to gather events for
+     * @param timeUnit the time unit of the duration
+     */
+    public TestSensorOperation(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs, long duration, TimeUnit timeUnit) {
+        this(context, sensorType, rateUs, maxBatchReportLatencyUs, null, duration, timeUnit);
+    }
+
+    /**
+     * Private helper constructor.
+     */
+    private TestSensorOperation(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs, Integer eventCount, Long duration, TimeUnit timeUnit) {
+        mContext = context;
+        mSensorType = sensorType;
+        mRateUs = rateUs;
+        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+        mEventCount = eventCount;
+        mDuration = duration;
+        mTimeUnit = timeUnit;
+        mSensorManager = new TestSensorManager(mContext, mSensorType, mRateUs,
+                mMaxBatchReportLatencyUs);
+    }
+
+    /**
+     * Set all of the default test expectations.
+     */
+    public void setDefaultVerifications() {
+        Sensor sensor = mSensorManager.getSensor();
+        addVerification(EventOrderingVerification.getDefault(sensor));
+        addVerification(FrequencyVerification.getDefault(sensor, mRateUs));
+        addVerification(JitterVerification.getDefault(sensor, mRateUs));
+        addVerification(MagnitudeVerification.getDefault(sensor));
+        addVerification(MeanVerification.getDefault(sensor));
+        // Skip SigNumVerification since it has no default
+        addVerification(StandardDeviationVerification.getDefault(sensor));
+    }
+
+    public void addVerification(ISensorVerification verification) {
+        if (verification != null) {
+            mVerifications.add(verification);
+        }
+    }
+
+    /**
+     * Collect the specified number of events from the sensor and run all enabled verifications.
+     */
+    @Override
+    public void execute() {
+        mStats.addValue("sensor_name", SensorTestInformation.getSensorName(mSensorType));
+        mStats.addValue("sensor_handle", mSensorManager.getSensor().getHandle());
+
+        ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
+
+        if (mEventCount != null) {
+            mSensorManager.runSensor(listener, mEventCount);
+        } else {
+            mSensorManager.runSensor(listener, mDuration, mTimeUnit);
+        }
+
+        boolean failed = false;
+        StringBuilder sb = new StringBuilder();
+
+        for (ISensorVerification verification : mVerifications) {
+            failed |= evaluateResults(verification, sb);
+        }
+
+        if (failed) {
+            Assert.fail(SensorCtsHelper.formatAssertionMessage(mSensorManager.getSensor(),
+                    "VerifySensorOperation", sb.toString()));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public TestSensorOperation clone() {
+        TestSensorOperation operation;
+        if (mEventCount != null) {
+            operation = new TestSensorOperation(mContext, mSensorType, mRateUs,
+                    mMaxBatchReportLatencyUs, mEventCount);
+        } else {
+            operation = new TestSensorOperation(mContext, mSensorType, mRateUs,
+                    mMaxBatchReportLatencyUs, mDuration, mTimeUnit);
+        }
+
+        for (ISensorVerification verification : mVerifications) {
+            operation.addVerification(verification.clone());
+        }
+        return operation;
+    }
+
+    /**
+     * Evaluate the results of a test, aggregate the stats, and build the error message.
+     */
+    private boolean evaluateResults(ISensorVerification verification, StringBuilder sb) {
+        try {
+            verification.verify(mStats);
+        } catch (AssertionError e) {
+            if (sb.length() > 0) {
+                sb.append(", ");
+            }
+            sb.append(e.getMessage());
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifySensorOperation.java
deleted file mode 100644
index 6f53573..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifySensorOperation.java
+++ /dev/null
@@ -1,654 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-import android.hardware.cts.helpers.TestSensorManager;
-import android.util.Log;
-
-import junit.framework.Assert;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
- * <p>
- * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
- * events and then run all the tests.
- * </p>
- */
-public class VerifySensorOperation extends AbstractSensorOperation {
-    private static final String TAG = "VerifySensorOperation";
-
-    private static final boolean DEBUG = false;
-
-    // threshold is (100 - 10)% expected to (100 + 110)% expected
-    private static final int[] DEFAULT_FREQUENCY_THRESHOLDS = {10, 110};
-
-    private TestSensorManager mSensorManager;
-    private Context mContext = null;
-    private int mSensorType = 0;
-    private int mRateUs = 0;
-    private int mMaxBatchReportLatencyUs = 0;
-    private Integer mEventCount = null;
-    private Long mDuration = null;
-    private TimeUnit mTimeUnit = null;
-
-    private boolean mVerifyEventOrdering = false;
-
-    private boolean mVerifyFrequency = false;
-    private double mFrequencyExpected = 0.0;
-    private double mFrequencyLowerThreshold = 0.0;
-    private double mFrequencyUpperThreshold = 0.0;
-
-    private boolean mVerifyJitter = false;
-    private int mJitterExpected = 0;
-    private int mJitterThreshold = 0;
-
-    private boolean mVerifyMean = false;
-    private float[] mMeanExpected = null;
-    private float[] mMeanThreshold = null;
-
-    private boolean mVerifyMagnitude = false;
-    private float mMagnitudeExpected = 0.0f;
-    private float mMagnitudeThreshold = 0.0f;
-
-    private boolean mVerifySignum = false;
-    private int[] mSignumExpected = null;
-    private float[] mSignumThreshold = null;
-
-    private boolean mVerifyStandardDeviation = false;
-    private float[] mStandardDeviationThreshold = null;
-
-    /**
-     * Create a {@link VerifySensorOperation}.
-     *
-     * @param context the {@link Context}.
-     * @param sensorType the sensor type
-     * @param rateUs the rate that
-     * @param maxBatchReportLatencyUs the max batch report latency
-     * @param eventCount the number of events to gather
-     */
-    public VerifySensorOperation(Context context, int sensorType, int rateUs,
-            int maxBatchReportLatencyUs, int eventCount) {
-        mContext = context;
-        mSensorType = sensorType;
-        mRateUs = rateUs;
-        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
-        mEventCount = eventCount;
-        mSensorManager = new TestSensorManager(mContext, mSensorType, mRateUs,
-                mMaxBatchReportLatencyUs);
-    }
-
-    /**
-     * Create a {@link VerifySensorOperation}.
-     *
-     * @param context the {@link Context}.
-     * @param sensorType the sensor type
-     * @param rateUs the rate that
-     * @param maxBatchReportLatencyUs the max batch report latency
-     * @param duration the duration to gather events for
-     * @param timeUnit the time unit of the duration
-     */
-    public VerifySensorOperation(Context context, int sensorType, int rateUs,
-            int maxBatchReportLatencyUs, long duration, TimeUnit timeUnit) {
-        mContext = context;
-        mSensorType = sensorType;
-        mRateUs = rateUs;
-        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
-        mDuration = duration;
-        mTimeUnit = timeUnit;
-        mSensorManager = new TestSensorManager(mContext, mSensorType, mRateUs,
-                mMaxBatchReportLatencyUs);
-    }
-
-    /**
-     * Set all of the default test expectations.
-     */
-    public void setDefaultVerifications() {
-        setDefaultVerifyEventOrdering();
-        setDefaultVerifyFrequency();
-        setDefaultVerifyJitter();
-        setDefaultVerifyMean();
-        setDefaultVerifyMagnitude();
-        setDefaultVerifySignum();
-        setDefaultVerifyStandardDeviation();
-    }
-
-    /**
-     * Enable the event ordering verification.
-     */
-    public void verifyEventOrdering() {
-        mVerifyEventOrdering = true;
-    }
-
-    /**
-     * Set the default event ordering verification.
-     */
-    @SuppressWarnings("deprecation")
-    public void setDefaultVerifyEventOrdering() {
-        switch (mSensorType) {
-            case Sensor.TYPE_ACCELEROMETER:
-            case Sensor.TYPE_MAGNETIC_FIELD:
-            case Sensor.TYPE_ORIENTATION:
-            case Sensor.TYPE_GYROSCOPE:
-            case Sensor.TYPE_PRESSURE:
-            case Sensor.TYPE_GRAVITY:
-            case Sensor.TYPE_LINEAR_ACCELERATION:
-            case Sensor.TYPE_ROTATION_VECTOR:
-            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
-            case Sensor.TYPE_GAME_ROTATION_VECTOR:
-            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
-            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
-                verifyEventOrdering();
-                break;
-        }
-    }
-
-    /**
-     * Enable the frequency verification.
-     *
-     * @param expected the expected frequency in Hz.
-     * @param threshold the threshold in Hz.
-     */
-    public void verifyFrequency(double expected, double threshold) {
-        mVerifyFrequency = true;
-        mFrequencyExpected = expected;
-        mFrequencyLowerThreshold = threshold;
-        mFrequencyUpperThreshold = threshold;
-    }
-
-    /**
-     * Enable the frequency verification.
-     *
-     * @param expected the expected frequency in Hz.
-     * @param lowerThreshold the lower threshold in Hz.
-     * @param upperThreshold the upper threshold in Hz.
-     */
-    public void verifyFrequency(double expected, double lowerThreshold, double upperThreshold) {
-        mVerifyFrequency = true;
-        mFrequencyExpected = expected;
-        mFrequencyLowerThreshold = lowerThreshold;
-        mFrequencyUpperThreshold = upperThreshold;
-    }
-
-    /**
-     * Set the default frequency verification depending on the sensor.
-     * <p>
-     * The expected frequency is based on {@link Sensor#getMinDelay()} and the threshold is
-     * calculated based on a percentage of the expected frequency.  The verification will not be run
-     * if the rate is set to {@link SensorManager#SENSOR_DELAY_GAME},
-     * {@link SensorManager#SENSOR_DELAY_UI}, or {@link SensorManager#SENSOR_DELAY_NORMAL} because
-     * these rates are not specified in the CDD.
-     */
-    @SuppressWarnings("deprecation")
-    public void setDefaultVerifyFrequency() {
-        if (!isRateValid()) {
-            return;
-        }
-
-        // sensorType: lowerThreshold, upperThreshold (% of expected frequency)
-        Map<Integer, int[]> defaults = new HashMap<Integer, int[]>(12);
-        defaults.put(Sensor.TYPE_ACCELEROMETER, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_MAGNETIC_FIELD, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_GYROSCOPE, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_ORIENTATION, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_PRESSURE, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_GRAVITY, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_LINEAR_ACCELERATION, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_ROTATION_VECTOR, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_GAME_ROTATION_VECTOR, DEFAULT_FREQUENCY_THRESHOLDS);
-        defaults.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, DEFAULT_FREQUENCY_THRESHOLDS);
-
-        if (defaults.containsKey(mSensorType)) {
-            // Expected frequency in Hz
-            double expected = SensorCtsHelper.getFrequency(
-                    SensorCtsHelper.getDelay(mSensorManager.getSensor(), mRateUs),
-                    TimeUnit.MICROSECONDS);
-            // Expected frequency * threshold percentage
-            double lowerThreshold = expected * defaults.get(mSensorType)[0] / 100;
-            double upperThreshold = expected * defaults.get(mSensorType)[1] / 100;
-            verifyFrequency(expected, lowerThreshold, upperThreshold);
-        }
-    }
-
-    /**
-     * Enable the jitter verification.
-     * <p>
-     * This test looks at the 95th percentile of the jitter and makes sure it is less than the
-     * threshold percentage of the expected period.
-     * </p>
-     *
-     * @param expected the expected period in ns.
-     * @param threshold the theshold as a percentage of the expected period.
-     */
-    public void verifyJitter(int expected, int threshold) {
-        mVerifyJitter = true;
-        mJitterExpected = expected;
-        mJitterThreshold = threshold;
-    }
-
-    /**
-     * Set the default jitter verification based on the sensor type.
-     * <p>
-     * The verification will not be run if the rate is set to
-     * {@link SensorManager#SENSOR_DELAY_GAME}, {@link SensorManager#SENSOR_DELAY_UI}, or
-     * {@link SensorManager#SENSOR_DELAY_NORMAL} because these rates are not specified in the CDD.
-     * </p>
-     */
-    @SuppressWarnings("deprecation")
-    public void setDefaultVerifyJitter() {
-        if (!isRateValid()) {
-            return;
-        }
-
-        // sensorType: threshold (% of expected period)
-        Map<Integer, Integer> defaults = new HashMap<Integer, Integer>(12);
-        // Sensors that we don't want to test at this time but still want to record the values.
-        defaults.put(Sensor.TYPE_ACCELEROMETER, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_MAGNETIC_FIELD, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_GYROSCOPE, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_ORIENTATION, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_PRESSURE, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_GRAVITY, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_LINEAR_ACCELERATION, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_ROTATION_VECTOR, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_GAME_ROTATION_VECTOR, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, Integer.MAX_VALUE);
-        defaults.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, Integer.MAX_VALUE);
-
-        if (defaults.containsKey(mSensorType)) {
-            int expected = (int) TimeUnit.NANOSECONDS.convert(
-                    SensorCtsHelper.getDelay(mSensorManager.getSensor(), mRateUs),
-                    TimeUnit.MICROSECONDS);
-            verifyJitter(expected, defaults.get(mSensorType));
-        }
-    }
-
-    /**
-     * Enable the mean verification.
-     *
-     * @param expected the expected means
-     * @param threshold the threshold
-     */
-    public void verifyMean(float[] expected, float[] threshold) {
-        mVerifyMean = true;
-        mMeanExpected = expected;
-        mMeanThreshold = threshold;
-    }
-
-    /**
-     * Set the default mean verification based on sensor type.
-     * <p>
-     * This sets the mean expectations for a device at rest in a standard environment. For sensors
-     * whose values vary depending on the orientation or environment, the expectations will not be
-     * set.
-     * </p><p>
-     * The following expectations are set for these sensors:
-     * </p><ul>
-     * <li>Gyroscope: all values should be 0.</li>
-     * <li>Pressure: values[0] should be close to the standard pressure.</li>
-     * <li>Linear acceleration: all values should be 0.</li>
-     * <li>Game rotation vector: all values should be 0 except values[3] which should be 1.</li>
-     * <li>Uncalibrated gyroscope: all values should be 0.</li>
-     * </ul>
-     */
-    @SuppressWarnings("deprecation")
-    public void setDefaultVerifyMean() {
-        // sensorType: {expected, threshold}
-        Map<Integer, Object[]> defaults = new HashMap<Integer, Object[]>(5);
-        // 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}});
-        // 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}});
-        // 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}});
-        // 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}});
-        // 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}});
-
-        if (defaults.containsKey(mSensorType)) {
-            float[] expected = (float[]) defaults.get(mSensorType)[0];
-            float[] threshold = (float[]) defaults.get(mSensorType)[1];
-            verifyMean(expected, threshold);
-        }
-    }
-
-    /**
-     * Enable the magnitude verification.
-     *
-     * @param expected the expected magnitude of the vector
-     * @param threshold the threshold
-     */
-    public void verifyMagnitude(float expected, float threshold) {
-        mVerifyMagnitude = true;
-        mMagnitudeExpected = expected;
-        mMagnitudeThreshold = threshold;
-    }
-
-    /**
-     * Set the default magnitude verification base on the sensor type.
-     * <p>
-     * This sets the magnitude expectations for a device at rest in a standard environment. For
-     * sensors whose values vary depending on the orientation or environment, the expectations will
-     * not be set.
-     * </p>
-     */
-    @SuppressWarnings("deprecation")
-    public void setDefaultVerifyMagnitude() {
-        // sensorType: {expected, threshold}
-        Map<Integer, Float[]> defaults = new HashMap<Integer, Float[]>(3);
-        defaults.put(Sensor.TYPE_ACCELEROMETER, new Float[]{SensorManager.STANDARD_GRAVITY, 1.5f});
-        defaults.put(Sensor.TYPE_GYROSCOPE, new Float[]{0.0f, 1.5f});
-        // Sensors that we don't want to test at this time but still want to record the values.
-        defaults.put(Sensor.TYPE_GRAVITY,
-                new Float[]{SensorManager.STANDARD_GRAVITY, Float.MAX_VALUE});
-
-        if (defaults.containsKey(mSensorType)) {
-            Float expected = defaults.get(mSensorType)[0];
-            Float threshold = defaults.get(mSensorType)[1];
-            verifyMagnitude(expected, threshold);
-        }
-    }
-
-    /**
-     * Enable the signum verification.
-     *
-     * @param expected the expected signs, an array of either -1s, 0s, or 1s.
-     * @param threshold the threshold
-     */
-    public void verifySignum(int[] expected, float[] threshold) {
-        mVerifySignum = true;
-        mSignumExpected = expected;
-        mSignumThreshold = threshold;
-    }
-
-    /**
-     * Set the default signum verification base on the sensor type.
-     * <p>
-     * This is a no-op since currently all sensors which can specify a default sign can also specify
-     * a default mean which is a more precise test.
-     * </p>
-     */
-    public void setDefaultVerifySignum() {
-        // No-op: All sensors that have an expected sign when static are already tested in
-        // setDefaultVerifyMean().
-    }
-
-    /**
-     * Enable the standard deviation verification.
-     *
-     * @param threshold the threshold.
-     */
-    public void verifyStandardDeviation(float[] threshold) {
-        mVerifyStandardDeviation = true;
-        mStandardDeviationThreshold = threshold;
-    }
-
-    /**
-     * Set the default standard deviation verification based on the sensor type.
-     */
-    @SuppressWarnings("deprecation")
-    public void setDefaultVerifyStandardDeviation() {
-        // sensorType: threshold
-        Map<Integer, float[]> defaults = new HashMap<Integer, float[]>(12);
-        defaults.put(Sensor.TYPE_ACCELEROMETER, new float[]{1.0f, 1.0f, 1.0f});
-        defaults.put(Sensor.TYPE_GYROSCOPE, new float[]{0.5f, 0.5f, 0.5f});
-        // Sensors that we don't want to test at this time but still want to record the values.
-        defaults.put(Sensor.TYPE_MAGNETIC_FIELD,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_ORIENTATION,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_PRESSURE,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_GRAVITY,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_LINEAR_ACCELERATION,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_ROTATION_VECTOR,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
-                Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
-                Float.MAX_VALUE, Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_GAME_ROTATION_VECTOR,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
-                Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
-                Float.MAX_VALUE, Float.MAX_VALUE});
-        defaults.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR,
-                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
-                Float.MAX_VALUE});
-
-        if (defaults.containsKey(mSensorType)) {
-            float[] threshold = defaults.get(mSensorType);
-            verifyStandardDeviation(threshold);
-        }
-    }
-
-    /**
-     * Collect the specified number of events from the sensor and run all enabled verifications.
-     */
-    @Override
-    public void execute() {
-        addValue("sensor_name", SensorTestInformation.getSensorName(mSensorType));
-        addValue("sensor_handle", mSensorManager.getSensor().getHandle());
-
-        TestSensorEvent[] events;
-        if (mEventCount != null) {
-            events = mSensorManager.collectEvents(mEventCount);
-        } else {
-            events = mSensorManager.collectEvents(mDuration, mTimeUnit);
-        }
-
-        boolean failed = false;
-        StringBuilder sb = new StringBuilder();
-        VerificationResult result = null;
-
-        if (mVerifyEventOrdering) {
-            result = SensorVerificationHelper.verifyEventOrdering(events);
-            // evaluateResults first so it is always called.
-            failed |= evaluateResults(result, sb);
-        }
-
-        if (mVerifyFrequency) {
-            result = SensorVerificationHelper.verifyFrequency(events, mFrequencyExpected,
-                    mFrequencyLowerThreshold, mFrequencyUpperThreshold);
-            failed |= evaluateResults(result, sb);
-        }
-
-        if (mVerifyJitter) {
-            result = SensorVerificationHelper.verifyJitter(events, mJitterExpected,
-                    mJitterThreshold);
-            failed |= evaluateResults(result, sb);
-        }
-
-        if (mVerifyMean) {
-            result = SensorVerificationHelper.verifyMean(events, mMeanExpected, mMeanThreshold);
-            failed |= evaluateResults(result, sb);
-        }
-
-        if (mVerifyMagnitude) {
-            result = SensorVerificationHelper.verifyMagnitude(events, mMagnitudeExpected,
-                    mMagnitudeThreshold);
-            failed |= evaluateResults(result, sb);
-        }
-
-        if (mVerifySignum) {
-            result = SensorVerificationHelper.verifySignum(events, mSignumExpected,
-                    mSignumThreshold);
-            failed |= evaluateResults(result, sb);
-        }
-
-        if (mVerifyStandardDeviation) {
-            result = SensorVerificationHelper.verifyStandardDeviation(events,
-                    mStandardDeviationThreshold);
-            failed |= evaluateResults(result, sb);
-        }
-
-        if (DEBUG) {
-            logStats(events);
-        }
-
-        if (failed) {
-            Assert.fail(String.format("%s, handle %d: %s",
-                    SensorTestInformation.getSensorName(mSensorType),
-                    mSensorManager.getSensor().getHandle(), sb.toString()));
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public VerifySensorOperation clone() {
-        VerifySensorOperation operation;
-        if (mEventCount != null) {
-            operation = new VerifySensorOperation(mContext, mSensorType, mRateUs,
-                    mMaxBatchReportLatencyUs, mEventCount);
-        } else {
-            operation = new VerifySensorOperation(mContext, mSensorType, mRateUs,
-                    mMaxBatchReportLatencyUs, mDuration, mTimeUnit);
-        }
-        if (mVerifyEventOrdering) {
-            operation.verifyEventOrdering();
-        }
-        if (mVerifyFrequency) {
-            operation.verifyFrequency(mFrequencyExpected, mFrequencyLowerThreshold,
-                    mFrequencyUpperThreshold);
-        }
-        if (mVerifyJitter) {
-            operation.verifyJitter(mJitterExpected, mJitterThreshold);
-        }
-        if (mVerifyMean) {
-            operation.verifyMean(mMeanExpected, mMeanThreshold);
-        }
-        if (mVerifyMagnitude) {
-            operation.verifyMagnitude(mMagnitudeExpected, mMagnitudeThreshold);
-        }
-        if (mVerifySignum) {
-            operation.verifySignum(mSignumExpected, mSignumThreshold);
-        }
-        if (mVerifyStandardDeviation) {
-            operation.verifyStandardDeviation(mStandardDeviationThreshold);
-        }
-        return operation;
-    }
-
-    /**
-     * Return true if the operation rate is not one of {@link SensorManager#SENSOR_DELAY_GAME},
-     * {@link SensorManager#SENSOR_DELAY_UI}, or {@link SensorManager#SENSOR_DELAY_NORMAL}.
-     */
-    private boolean isRateValid() {
-        return (mRateUs != SensorManager.SENSOR_DELAY_GAME
-                && mRateUs != SensorManager.SENSOR_DELAY_UI
-                && mRateUs != SensorManager.SENSOR_DELAY_NORMAL);
-    }
-
-    /**
-     * Evaluate the results of a test, aggregate the stats, and build the error message.
-     */
-    private boolean evaluateResults(VerificationResult result, StringBuilder sb) {
-        for (String key : result.getKeys()) {
-            addValue(key, result.getValue(key));
-        }
-
-        if (result.isFailed()) {
-            if (sb.length() > 0) {
-                sb.append(", ");
-            }
-            sb.append(result.getFailureMessage());
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Log the events to the logcat.
-     */
-    private void logStats(TestSensorEvent[] events) {
-        if (events.length <= 0) {
-            return;
-        }
-
-        List<Double> jitterValues = null;
-        if (events.length > 1) {
-            jitterValues = SensorCtsHelper.getJitterValues(events);
-        }
-
-        logTestSensorEvent(0, events[0], null, null);
-        for (int i = 1; i < events.length; i++) {
-            Double jitter = jitterValues == null ? null : jitterValues.get(i - 1);
-            logTestSensorEvent(i, events[i], events[i - 1], jitter);
-        }
-    }
-
-    /**
-     * Log a single sensor event to the logcat.
-     */
-    private void logTestSensorEvent(int index, TestSensorEvent event, TestSensorEvent prevEvent,
-            Double jitter) {
-        String deltaStr = prevEvent == null ? null : String.format("%d",
-                event.timestamp - prevEvent.timestamp);
-        String jitterStr = jitter == null ? null : String.format("%.2f", jitter);
-
-        StringBuilder valuesSb = new StringBuilder();
-        if (event.values.length == 1) {
-            valuesSb.append(String.format("%.2f", event.values[0]));
-        } else {
-            valuesSb.append("[").append(String.format("%.2f", event.values[0]));
-            for (int i = 1; i < event.values.length; i++) {
-                valuesSb.append(String.format(", %.2f", event.values[i]));
-            }
-            valuesSb.append("]");
-        }
-
-        Log.v(TAG, String.format(
-                "Sensor %d: Event %d: device_timestamp=%d, delta_timestamp=%s, jitter=%s, "
-                + "values=%s", mSensorManager.getSensor().getType(), index, event.timestamp,
-                deltaStr, jitterStr, valuesSb.toString()));
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractMeanVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractMeanVerification.java
new file mode 100644
index 0000000..8d132a3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractMeanVerification.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+/**
+ * Abstract class that calculates of the mean event values.
+ */
+public abstract class AbstractMeanVerification extends AbstractSensorVerification {
+    private float[] mSums = null;
+    private int mCount = 0;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mSums == null) {
+            mSums = new float[event.values.length];
+        }
+        Assert.assertEquals(mSums.length, event.values.length);
+        for (int i = 0; i < mSums.length; i++) {
+            mSums[i] += event.values[i];
+        }
+        mCount++;
+    }
+
+    /**
+     * Return the number of events.
+     */
+    protected int getCount() {
+        return mCount;
+    }
+
+    /**
+     * Return the means of the event values.
+     */
+    protected float[] getMeans() {
+        if (mCount < 0) {
+            return null;
+        }
+
+        float[] means = new float[mSums.length];
+        for (int i = 0; i < mSums.length; i++) {
+            means[i] = mSums[i] / mCount;
+        }
+        return means;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
new file mode 100644
index 0000000..f11f7a4
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.TestSensorEvent;
+
+/**
+ * Abstract class that deals with the synchronization of the sensor verifications.
+ */
+public abstract class AbstractSensorVerification implements ISensorVerification {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void addSensorEvents(TestSensorEvent ... events) {
+        for (TestSensorEvent event : events) {
+            addSensorEventInternal(event);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void addSensorEvent(TestSensorEvent event) {
+        addSensorEventInternal(event);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public abstract ISensorVerification clone();
+
+    /**
+     * Used by implementing classes to add a sensor event.
+     */
+    protected abstract void addSensorEventInternal(TestSensorEvent event);
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
new file mode 100644
index 0000000..28ddfe7
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A {@link ISensorVerification} which verifies that all events are received in the correct order.
+ */
+public class EventOrderingVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "event_order_passed";
+
+    private static final int MESSAGE_LENGTH = 3;
+
+    private Long mMaxTimestamp = null;
+    private TestSensorEvent mPreviousEvent = null;
+    private final List<EventInfo> mOutOfOrderEvents = new LinkedList<EventInfo>();
+    private int mCount = 0;
+    private int mIndex = 0;
+
+    /**
+     * Get the default {@link EventOrderingVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    @SuppressWarnings("deprecation")
+    public static EventOrderingVerification getDefault(Sensor sensor) {
+        switch (sensor.getType()) {
+            case Sensor.TYPE_ACCELEROMETER:
+            case Sensor.TYPE_MAGNETIC_FIELD:
+            case Sensor.TYPE_ORIENTATION:
+            case Sensor.TYPE_GYROSCOPE:
+            case Sensor.TYPE_PRESSURE:
+            case Sensor.TYPE_GRAVITY:
+            case Sensor.TYPE_LINEAR_ACCELERATION:
+            case Sensor.TYPE_ROTATION_VECTOR:
+            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+            case Sensor.TYPE_GAME_ROTATION_VECTOR:
+            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
+            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+                return new EventOrderingVerification();
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Verify that the events are in the correct order.  Add {@value #PASSED_KEY},
+     * {@value SensorStats#EVENT_OUT_OF_ORDER_COUNT_KEY}, and
+     * {@value SensorStats#EVENT_OUT_OF_ORDER_POSITIONS_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        stats.addValue(PASSED_KEY, mCount == 0);
+        stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY, mCount);
+
+        int[] indices = new int[mOutOfOrderEvents.size()];
+        for (int i = 0; i < indices.length; i++) {
+            indices[i] = mOutOfOrderEvents.get(i).index;
+        }
+        stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY, indices);
+
+        if (mOutOfOrderEvents.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(mCount).append(" events out of order: ");
+            for (int i = 0; i < Math.min(mOutOfOrderEvents.size(), MESSAGE_LENGTH); i++) {
+                EventInfo info = mOutOfOrderEvents.get(i);
+                sb.append(String.format("position=%d, previous=%d, timestamp=%d; ", info.index,
+                        info.previousEvent.timestamp, info.event.timestamp));
+            }
+            if (mOutOfOrderEvents.size() > MESSAGE_LENGTH) {
+                sb.append(mOutOfOrderEvents.size() - MESSAGE_LENGTH).append(" more");
+            } else {
+                // Delete the trailing "; "
+                sb.delete(sb.length() - 2, sb.length());
+            }
+
+            Assert.fail(sb.toString());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public EventOrderingVerification clone() {
+        return new EventOrderingVerification();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mPreviousEvent == null) {
+            mMaxTimestamp = event.timestamp;
+        } else {
+            if (event.timestamp < mMaxTimestamp) {
+                mOutOfOrderEvents.add(new EventInfo(mIndex, event, mPreviousEvent));
+                mCount++;
+            } else if (event.timestamp > mMaxTimestamp) {
+                mMaxTimestamp = event.timestamp;
+            }
+        }
+
+        mPreviousEvent = event;
+        mIndex++;
+    }
+
+    private class EventInfo {
+        public final int index;
+        public final TestSensorEvent event;
+        public final TestSensorEvent previousEvent;
+
+        public EventInfo(int index, TestSensorEvent event, TestSensorEvent previousEvent) {
+            this.index = index;
+            this.event = event;
+            this.previousEvent = previousEvent;
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
new file mode 100644
index 0000000..28cbd01
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link EventOrderingVerification}.
+ */
+public class EventOrderingVerificationTest extends TestCase {
+
+    /**
+     * Test that the verification passes when there are no results.
+     */
+    public void testNoEvents() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification();
+        verification.verify(stats);
+        verifyStats(stats, true, 0);
+    }
+
+    /**
+     * Test that the verification passes when the timestamps are the same.
+     */
+    public void testSameTimestamp() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(0, 0, 0, 0, 0);
+        verification.verify(stats);
+        verifyStats(stats, true, 0);
+    }
+
+    /**
+     * Test that the verification passes when the timestamps are increasing.
+     */
+    public void testSequentialTimestamp() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(0, 1, 2, 3, 4);
+        verification.verify(stats);
+        verifyStats(stats, true, 0);
+    }
+
+    /**
+     * Test that the verification fails when there is one event out of order.
+     */
+    public void testSingleOutofOrder() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(0, 2, 1, 3, 4);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 1);
+        List<Integer> indices = getIndices(stats);
+        assertTrue(indices.contains(2));
+    }
+
+    /**
+     * Test that the verification fails when there are multiple events out of order.
+     */
+    public void testMultipleOutOfOrder() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(4, 0, 1, 2, 3);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 4);
+        List<Integer> indices = getIndices(stats);
+        assertTrue(indices.contains(1));
+        assertTrue(indices.contains(2));
+        assertTrue(indices.contains(3));
+        assertTrue(indices.contains(4));
+    }
+
+    private ISensorVerification getVerification(long ... timestamps) {
+        ISensorVerification verification = new EventOrderingVerification();
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, int count) {
+        assertEquals(passed, stats.getValue(EventOrderingVerification.PASSED_KEY));
+        assertEquals(count, stats.getValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY));
+        assertNotNull(stats.getValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY));
+    }
+
+    private List<Integer> getIndices(SensorStats stats) {
+        int[] primitiveIndices = (int[]) stats.getValue(
+                SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY);
+        List<Integer> indices = new ArrayList<Integer>(primitiveIndices.length);
+        for (int index : primitiveIndices) {
+            indices.add(index);
+        }
+        return indices;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
new file mode 100644
index 0000000..030bfeb
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 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.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the sensor frequency are within the expected
+ * range.
+ */
+public class FrequencyVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "frequency_passed";
+
+    // threshold is (100 - 10)% expected to (100 + 110)% expected
+    private static final int[] DEFAULT_THRESHOLDS = {10, 110};
+
+    // sensorType: lowerThreshold, upperThreshold (% of expected frequency)
+    private final static Map<Integer, int[]> DEFAULTS = new HashMap<Integer, int[]>(12);
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    private final double mExpected;
+    private final double mLowerThreshold;
+    private final double mUpperThreshold;
+
+    private long mMinTimestamp = 0;
+    private long mMaxTimestamp = 0;
+    private int mCount = 0;
+
+    /**
+     * Construct a {@link FrequencyVerification}.
+     *
+     * @param expected the expected frequency in Hz.
+     * @param lowerTheshold the lower threshold in Hz. {@code expected - lower} should be the
+     * slowest acceptable frequency of the sensor.
+     * @param upperThreshold the upper threshold in Hz. {@code expected + upper} should be the
+     * fastest acceptable frequency of the sensor.
+     */
+    public FrequencyVerification(double expected, double lowerTheshold, double upperThreshold) {
+        mExpected = expected;
+        mLowerThreshold = lowerTheshold;
+        mUpperThreshold = upperThreshold;
+    }
+
+    /**
+     * Get the default {@link FrequencyVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @param rateUs the desired rate of the sensor
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static FrequencyVerification getDefault(Sensor sensor, int rateUs) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+
+        // Expected frequency in Hz
+        double expected = SensorCtsHelper.getFrequency(SensorCtsHelper.getDelay(sensor, rateUs),
+                TimeUnit.MICROSECONDS);
+        // Expected frequency * threshold percentage
+        double lowerThreshold = expected * DEFAULTS.get(sensor.getType())[0] / 100;
+        double upperThreshold = expected * DEFAULTS.get(sensor.getType())[1] / 100;
+        return new FrequencyVerification(expected, lowerThreshold, upperThreshold);
+    }
+
+    /**
+     * Verify that the frequency is correct. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#FREQUENCY_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mCount < 2) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        double frequency = SensorCtsHelper.getFrequency(
+                ((double) (mMaxTimestamp - mMinTimestamp)) / (mCount - 1), TimeUnit.NANOSECONDS);
+        boolean failed = (frequency <= mExpected - mLowerThreshold
+                || frequency >= mExpected + mUpperThreshold);
+
+        stats.addValue(SensorStats.FREQUENCY_KEY, frequency);
+        stats.addValue(PASSED_KEY, !failed);
+
+        if (failed) {
+            Assert.fail(String.format("Frequency out of range: frequency=%.2fHz, "
+                    + "expected=(%.2f-%.2fHz, %.2f+%.2fHz)", frequency, mExpected, mLowerThreshold,
+                    mExpected, mUpperThreshold));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FrequencyVerification clone() {
+        return new FrequencyVerification(mExpected, mLowerThreshold, mUpperThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mCount == 0) {
+            mMinTimestamp = event.timestamp;
+            mMaxTimestamp = event.timestamp;
+        } else {
+            if (mMinTimestamp > event.timestamp) {
+                mMinTimestamp = event.timestamp;
+            }
+            if (mMaxTimestamp < event.timestamp) {
+                mMaxTimestamp = event.timestamp;
+            }
+        }
+        mCount++;
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_ORIENTATION, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_PRESSURE, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_GRAVITY, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR, DEFAULT_THRESHOLDS);
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, DEFAULT_THRESHOLDS);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
new file mode 100644
index 0000000..cec09a5
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link EventOrderingVerification}.
+ */
+public class FrequencyVerificationTest extends TestCase {
+
+    /**
+     * Test that the verifications passes/fails based on threshold given.
+     */
+    public void testVerifification() {
+        long[] timestamps = {0, 1000000, 2000000, 3000000, 4000000};  // 1000Hz
+
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(1000.0, 1.0, 1.0, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(950.0, 100.0, 100.0, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(1050.0, 100.0, 100.0, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(950.0, 100.0, 25.0, timestamps);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(1050.0, 25.0, 100.0, timestamps);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 1000.0);
+    }
+
+    private ISensorVerification getVerification(double expected, double lowerThreshold,
+            double upperThreshold, long ... timestamps) {
+        ISensorVerification verification = new FrequencyVerification(expected, lowerThreshold,
+                upperThreshold);
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, double frequency) {
+        assertEquals(passed, stats.getValue(FrequencyVerification.PASSED_KEY));
+        assertEquals(frequency, stats.getValue(SensorStats.FREQUENCY_KEY));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
new file mode 100644
index 0000000..07af392
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+/**
+ * Interface describing the sensor verification. This class was designed for to handle streaming
+ * events. The methods {@link #addSensorEvent(TestSensorEvent)} and
+ * {@link #addSensorEvents(TestSensorEvent...)} should be called in the order that the events are
+ * received. The method {@link #verify(SensorStats)} should be called after all events are added.
+ */
+public interface ISensorVerification {
+
+    /**
+     * Add a single {@link TestSensorEvent} to be evaluated.
+     */
+    public void addSensorEvent(TestSensorEvent event);
+
+    /**
+     * Add multiple {@link TestSensorEvent}s to be evaluated.
+     */
+    public void addSensorEvents(TestSensorEvent ... events);
+
+    /**
+     * Evaluate all added {@link TestSensorEvent}s and update stats.
+     *
+     * @param stats a {@link SensorStats} object used to keep track of the stats.
+     * @throws AssertionError if the verification fails.
+     */
+    public void verify(SensorStats stats);
+
+    /**
+     * Clones the {@link ISensorVerification}
+     */
+    public ISensorVerification clone();
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
new file mode 100644
index 0000000..7929ee9
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 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.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the sensor jitter is in an acceptable range.
+ */
+public class JitterVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "jitter_passed";
+
+    // sensorType: threshold (% of expected period)
+    private static final Map<Integer, Integer> DEFAULTS = new HashMap<Integer, Integer>(12);
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    private final int mExpected;
+    private final int mThreshold;
+
+    private List<Long> mTimestamps = new LinkedList<Long>();
+
+    /**
+     * Construct a {@link JitterVerification}
+     *
+     * @param expected the expected period in ns
+     * @param threshold the acceptable margin of error as a percentage
+     */
+    public JitterVerification(int expected, int threshold) {
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link JitterVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @param rateUs the desired rate of the sensor
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static JitterVerification getDefault(Sensor sensor, int rateUs) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+
+        int expected = (int) TimeUnit.NANOSECONDS.convert(SensorCtsHelper.getDelay(sensor, rateUs),
+                TimeUnit.MICROSECONDS);
+        return new JitterVerification(expected, DEFAULTS.get(sensor.getType()));
+    }
+
+    /**
+     * Verify that the 95th percentile of the jitter is in the acceptable range. Add
+     * {@value #PASSED_KEY} and {@value SensorStats#JITTER_95_PERCENTILE_KEY} keys to
+     * {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mTimestamps.size() < 2) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        List<Double> jitters = getJitterValues();
+        double jitter95Percentile = SensorCtsHelper.get95PercentileValue(jitters);
+        boolean failed = (jitter95Percentile > mExpected * (mThreshold / 100.0));
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.JITTER_95_PERCENTILE_KEY, jitter95Percentile);
+
+        if (failed) {
+            Assert.fail(String.format("Jitter out of range: jitter at 95th percentile=%.0fns, "
+                    + "expected=<%.0fns", jitter95Percentile, mExpected * (mThreshold / 100.0)));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public JitterVerification clone() {
+        return new JitterVerification(mExpected, mThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        mTimestamps.add(event.timestamp);
+    }
+
+    /**
+     * Get the list of all jitter values. Exposed for unit testing.
+     */
+    List<Double> getJitterValues() {
+        List<Long> deltas = new ArrayList<Long>(mTimestamps.size() - 1);
+        for (int i = 1; i < mTimestamps.size(); i++) {
+            deltas.add(mTimestamps.get(i) - mTimestamps.get(i -1));
+        }
+        double deltaMean = SensorCtsHelper.getMean(deltas);
+        List<Double> jitters = new ArrayList<Double>(deltas.size());
+        for (long delta : deltas) {
+            jitters.add(Math.abs(delta - deltaMean));
+        }
+        return jitters;
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        // Sensors that we don't want to test at this time but still want to record the values.
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_ORIENTATION, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_PRESSURE, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GRAVITY, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, Integer.MAX_VALUE);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
new file mode 100644
index 0000000..a9e872a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+/**
+ * Tests for {@link EventOrderingVerification}.
+ */
+public class JitterVerificationTest extends TestCase {
+
+
+    public void testVerify() {
+        final int SAMPLE_SIZE = 100;
+
+        // 100 samples at 1000Hz
+        long[] timestamps = new long[SAMPLE_SIZE];
+        for (int i = 0; i < SAMPLE_SIZE; i++) {
+            timestamps[i] = i * 100000;
+        }
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(1000, 1, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 0.0);
+
+        // 90 samples at 1000Hz, 10 samples at 2000Hz
+        long timestamp = 0;
+        for (int i = 0; i < SAMPLE_SIZE; i++) {
+            timestamps[i] = timestamp;
+            timestamp += (i % 10 == 0) ? 500000 : 1000000;
+        }
+        stats = new SensorStats();
+        verification = getVerification(1000, 1, timestamps);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 449494.9494);
+    }
+
+    public void testCalculateJitter() {
+        long[] timestamps = new long[]{0, 1, 2, 3, 4};
+        JitterVerification verification = getVerification(1000, 1, timestamps);
+        List<Double> jitterValues = verification.getJitterValues();
+        assertEquals(4, jitterValues.size());
+        assertEquals(0.0, (double) jitterValues.get(0));
+        assertEquals(0.0, (double) jitterValues.get(1));
+        assertEquals(0.0, (double) jitterValues.get(2));
+        assertEquals(0.0, (double) jitterValues.get(3));
+
+        timestamps = new long[]{0, 0, 2, 4, 4};
+        verification = getVerification(1000, 1, timestamps);
+        jitterValues = verification.getJitterValues();
+        assertEquals(4, jitterValues.size());
+        assertEquals(1.0, (double) jitterValues.get(0));
+        assertEquals(1.0, (double) jitterValues.get(1));
+        assertEquals(1.0, (double) jitterValues.get(2));
+        assertEquals(1.0, (double) jitterValues.get(3));
+
+        timestamps = new long[]{0, 1, 4, 9, 16};
+        verification = getVerification(1000, 1, timestamps);
+        jitterValues = verification.getJitterValues();
+        assertEquals(4, jitterValues.size());
+        assertEquals(4, jitterValues.size());
+        assertEquals(3.0, (double) jitterValues.get(0));
+        assertEquals(1.0, (double) jitterValues.get(1));
+        assertEquals(1.0, (double) jitterValues.get(2));
+        assertEquals(3.0, (double) jitterValues.get(3));
+    }
+
+    private JitterVerification getVerification(int expected, int threshold, long ... timestamps) {
+        JitterVerification verification = new JitterVerification(expected, threshold);
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, double jitter95) {
+        assertEquals(passed, stats.getValue(JitterVerification.PASSED_KEY));
+        assertEquals(jitter95, (Double) stats.getValue(SensorStats.JITTER_95_PERCENTILE_KEY), 0.1);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerification.java
new file mode 100644
index 0000000..d987eef
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerification.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link ISensorVerification} which verifies that the mean of the magnitude of the sensors vector
+ * is within the expected range.
+ */
+public class MagnitudeVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "magnitude_passed";
+
+    // sensorType: {expected, threshold}
+    private static Map<Integer, Float[]> DEFAULTS = new HashMap<Integer, Float[]>(3);
+    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 float mSum = 0.0f;
+    private int mCount = 0;
+
+    /**
+     * Construct a {@link MagnitudeVerification}
+     *
+     * @param expected the expected value
+     * @param threshold the threshold
+     */
+    public MagnitudeVerification(float expected, float threshold) {
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link MagnitudeVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static MagnitudeVerification getDefault(Sensor sensor) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+        Float expected = DEFAULTS.get(sensor.getType())[0];
+        Float threshold = DEFAULTS.get(sensor.getType())[1];
+        return new MagnitudeVerification(expected, threshold);
+    }
+
+    /**
+     * Verify that the magnitude is in the acceptable range. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#MAGNITUDE_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mCount < 1) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        float mean = mSum / mCount;
+        boolean failed = Math.abs(mean - mExpected) > mThreshold;
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.MAGNITUDE_KEY, mean);
+
+        if (failed) {
+            Assert.fail(String.format("Magnitude mean out of range: mean=%s, expected=%s+/-%s",
+                    mean, mExpected, mThreshold));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public MagnitudeVerification clone() {
+        return new MagnitudeVerification(mExpected, mThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        float sumOfSquares = 0.0f;
+        for (float value : event.values) {
+            sumOfSquares += value * value;
+        }
+        mSum += (float) Math.sqrt(sumOfSquares);
+        mCount++;
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new Float[]{SensorManager.STANDARD_GRAVITY, 1.5f});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new Float[]{0.0f, 1.5f});
+        // Sensors that we don't want to test at this time but still want to record the values.
+        DEFAULTS.put(Sensor.TYPE_GRAVITY,
+                new Float[]{SensorManager.STANDARD_GRAVITY, Float.MAX_VALUE});
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
new file mode 100644
index 0000000..9a50753
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link MagnitudeVerification}.
+ */
+public class MagnitudeVerificationTest extends TestCase {
+
+    /**
+     * Test {@link MagnitudeVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        float[][] values = {
+                {0, 3, 4},
+                {4, 0, 3},
+                {3, 4, 0},
+                {0, 0, 4},
+                {6, 0, 0},
+        };
+
+        runStats(5.0f, 0.1f, values, true, 5.0f);
+        runStats(4.5f, 0.6f, values, true, 5.0f);
+        runStats(5.5f, 0.6f, values, true, 5.0f);
+        runStats(4.5f, 0.1f, values, false, 5.0f);
+        runStats(5.5f, 0.1f, values, false, 5.0f);
+    }
+
+    private void runStats(float expected, float threshold, float[][] values, boolean pass, float magnitude) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, threshold, values);
+        if (pass) {
+            verification.verify(stats);
+        } else {
+            try {
+                verification.verify(stats);
+                fail("Expected an AssertionError");
+            } catch (AssertionError e) {
+                // Expected;
+            }
+        }
+        assertEquals(pass, stats.getValue(MagnitudeVerification.PASSED_KEY));
+        assertEquals(magnitude, (Float) stats.getValue(SensorStats.MAGNITUDE_KEY), 0.01);
+    }
+
+    private ISensorVerification getVerification(float expected, float threshold,
+            float[] ... values) {
+        ISensorVerification verification = new MagnitudeVerification(expected, threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java
new file mode 100644
index 0000000..315b3aa
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorStats;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link ISensorVerification} which verifies that the means matches the expected measurement.
+ */
+public class MeanVerification extends AbstractMeanVerification {
+    public static final String PASSED_KEY = "mean_passed";
+
+    // sensorType: {expected, threshold}
+    private static final Map<Integer, Object[]> DEFAULTS = new HashMap<Integer, Object[]>(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;
+
+    /**
+     * Construct a {@link MeanVerification}
+     *
+     * @param expected the expected values
+     * @param threshold the thresholds
+     */
+    public MeanVerification(float[] expected, float[] threshold) {
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link MeanVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static MeanVerification getDefault(Sensor sensor) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+        float[] expected = (float[]) DEFAULTS.get(sensor.getType())[0];
+        float[] threshold = (float[]) DEFAULTS.get(sensor.getType())[1];
+        return new MeanVerification(expected, threshold);
+    }
+
+    /**
+     * Verify that the mean is in the acceptable range. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#MEAN_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (getCount() < 1) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        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]) {
+                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(")");
+        }
+
+        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()));
+        }
+    }
+
+    @Override
+    public MeanVerification clone() {
+        return new MeanVerification(mExpected, mThreshold);
+    }
+
+    @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}});
+        // 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}});
+        // 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}});
+        // 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}});
+        // 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}});
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
new file mode 100644
index 0000000..94b6362
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link MeanVerification}.
+ */
+public class MeanVerificationTest extends TestCase {
+
+    /**
+     * Test {@link MeanVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        float[][] values = {
+                {0, 1, 0},
+                {1, 2, 1},
+                {2, 3, 4},
+                {3, 4, 9},
+                {4, 5, 16},
+        };
+
+        float[] expected = {2.0f, 3.0f, 6.0f};
+        float[] threshold = {0.1f, 0.1f, 0.1f};
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, threshold, values);
+        verification.verify(stats);
+        verifyStats(stats, true, new float[]{2.0f, 3.0f, 6.0f});
+
+        expected = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.6f, 0.6f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        verification.verify(stats);
+        verifyStats(stats, true, new float[]{2.0f, 3.0f, 6.0f});
+
+        expected = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.1f, 0.6f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+
+        expected = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.6f, 0.1f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+
+        threshold = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.6f, 0.6f, 0.1f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+    }
+
+    private ISensorVerification getVerification(float[] expected, float[] threshold,
+            float[] ... values) {
+        ISensorVerification verification = new MeanVerification(expected, threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, float[] means) {
+        assertEquals(passed, stats.getValue(MeanVerification.PASSED_KEY));
+        float[] actual = (float[]) stats.getValue(SensorStats.MEAN_KEY);
+        for (int i = 0; i < means.length; i++) {
+            assertEquals(means[i], actual[i], 0.1);
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerification.java
new file mode 100644
index 0000000..9417cd3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerification.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+
+import junit.framework.Assert;
+
+/**
+ * A {@link ISensorVerification} which verifies that the sign of each of the sensor values is
+ * correct.
+ * <p>
+ * If the value of the measurement is in [-threshold, threshold], the sign is considered 0. If
+ * it is less than -threshold, it is considered -1. If it is greater than threshold, it is
+ * considered 1.
+ * </p>
+ */
+public class SigNumVerification extends AbstractMeanVerification {
+    public static final String PASSED_KEY = "sig_num_passed";
+
+    private final int[] mExpected;
+    private final float[] mThreshold;
+
+    /**
+     * Construct a {@link SigNumVerification}
+     *
+     * @param expected the expected values
+     * @param threshold the threshold that needs to be crossed to consider a measurement nonzero
+     * @throws IllegalStateException if the expected values are not 0, -1, or 1.
+     */
+    public SigNumVerification(int[] expected, float[] threshold) {
+        for (int i = 0; i < expected.length; i++) {
+            if (!(expected[i] == -1 || expected[i] == 0 || expected[i] == 1)) {
+                throw new IllegalArgumentException("Expected value must be -1, 0, or 1");
+            }
+        }
+
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Verify that the sign of each of the sensor values is correct. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#MEAN_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (getCount() < 1) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        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++) {
+            meanSb.append(String.format("%.2f", means[i]));
+            if (i != means.length - 1) meanSb.append(", ");
+
+            if (mExpected[i] == 0) {
+                if (Math.abs(means[i]) > mThreshold[i]) {
+                    failed = true;
+                }
+                expectedSb.append(String.format("[%.2f, %.2f]", -mThreshold[i], mThreshold[i]));
+            } else {
+                if (mExpected[i] > 0) {
+                    if (means[i] <= mThreshold[i]) {
+                        failed = true;
+                    }
+                    expectedSb.append(String.format("(%.2f, inf)", mThreshold[i]));
+                } else {
+                    if (means[i] >= -1 * mThreshold[i]) {
+                        failed = true;
+                    }
+                    expectedSb.append(String.format("(-inf, %.2f)", -1 * mThreshold[i]));
+                }
+            }
+            if (i != means.length - 1) expectedSb.append(", ");
+        }
+        if (means.length > 1) {
+            meanSb.append(")");
+            expectedSb.append(")");
+        }
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.MEAN_KEY, means);
+
+        if (failed) {
+            Assert.fail(String.format("Signum out of range: mean=%s, expected=%s",
+                    meanSb.toString(), expectedSb.toString()));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SigNumVerification clone() {
+        return new SigNumVerification(mExpected, mThreshold);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerificationTest.java
new file mode 100644
index 0000000..009ab65
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerificationTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link SigNumVerification}.
+ */
+public class SigNumVerificationTest extends TestCase {
+
+    /**
+     * Test {@link SigNumVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        float[][] values = {{1.0f, 0.2f, 0.0f, -0.2f, -1.0f}};
+
+        int[] expected = {1, 1, 0, -1, -1};
+        float[] threshold = {0.1f, 0.1f, 0.1f, 0.1f, 0.1f};
+        runVerification(true, expected, threshold, values);
+
+        expected = new int[]{1, 0, 0, 0, -1};
+        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
+        runVerification(true, expected, threshold, values);
+
+        expected = new int[]{0, 1, 0, -1, 0};
+        threshold = new float[]{1.5f, 0.1f, 0.1f, 0.1f, 1.5f};
+        runVerification(true, expected, threshold, values);
+
+        expected = new int[]{1, 0, 0, 0, 1};
+        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
+        runVerification(false, expected, threshold, values);
+
+        expected = new int[]{-1, 0, 0, 0, -1};
+        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
+        runVerification(false, expected, threshold, values);
+    }
+
+    private SigNumVerification getVerification(int[] expected, float[] threshold,
+            float[] ... values) {
+        SigNumVerification verification = new SigNumVerification(expected, threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+
+    private void runVerification(boolean passed, int[] expected, float[] threshold,
+            float[][] values) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, threshold, values);
+        if (passed) {
+            verification.verify(stats);
+        } else {
+            try {
+                verification.verify(stats);
+                fail("Expected an AssertionError");
+            } catch (AssertionError e) {
+                // Expected;
+            }
+        }
+        assertEquals(passed, stats.getValue(SigNumVerification.PASSED_KEY));
+        assertNotNull(stats.getValue(SensorStats.MEAN_KEY));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
new file mode 100644
index 0000000..03db067
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link ISensorVerification} which verifies that the standard deviations is within the expected
+ * range.
+ */
+public class StandardDeviationVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "standard_deviation_passed";
+
+    // sensorType: threshold
+    private static final Map<Integer, float[]> DEFAULTS = new HashMap<Integer, float[]>(12);
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    private final float[] mThreshold;
+
+    private float[] mMeans = null;
+    private float[] mM2s = null;
+    private int mCount = 0;
+
+    /**
+     * Construct a {@link StandardDeviationVerification}
+     *
+     * @param threshold the thresholds
+     */
+    public StandardDeviationVerification(float[] threshold) {
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link StandardDeviationVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static StandardDeviationVerification getDefault(Sensor sensor) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+
+        return new StandardDeviationVerification(DEFAULTS.get(sensor.getType()));
+    }
+
+    /**
+     * Verify that the standard deviation is in the acceptable range. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#STANDARD_DEVIATION_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mCount < 2) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        float[] stdDevs = new float[mM2s.length];
+        for (int i = 0; i < mM2s.length; i++) {
+            stdDevs[i] = (float) Math.sqrt(mM2s[i] / (mCount - 1));
+        }
+
+        boolean failed = false;
+        StringBuilder stddevSb = new StringBuilder();
+        StringBuilder expectedSb = new StringBuilder();
+
+        if (stdDevs.length > 1) {
+            stddevSb.append("(");
+            expectedSb.append("(");
+        }
+        for (int i = 0; i < stdDevs.length; i++) {
+            if (stdDevs[i] > mThreshold[i]) {
+                failed = true;
+            }
+            stddevSb.append(String.format("%.2f", stdDevs[i]));
+            if (i != stdDevs.length - 1) stddevSb.append(", ");
+            expectedSb.append(String.format("<%.2f", mThreshold[i]));
+            if (i != stdDevs.length - 1) expectedSb.append(", ");
+        }
+        if (stdDevs.length > 1) {
+            stddevSb.append(")");
+            expectedSb.append(")");
+        }
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.STANDARD_DEVIATION_KEY, stdDevs);
+
+        if (failed) {
+            Assert.fail(String.format("Standard deviation out of range: stddev=%s, expected=%s",
+                    stddevSb.toString(), expectedSb.toString()));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public StandardDeviationVerification clone() {
+        return new StandardDeviationVerification(mThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Computes the standard deviation using
+     * <a href="http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm">
+     * Welford's algorith</a>.
+     * </p>
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mMeans == null || mM2s == null) {
+            mMeans = new float[event.values.length];
+            mM2s = new float[event.values.length];
+        }
+
+        Assert.assertEquals(mMeans.length, event.values.length);
+        Assert.assertEquals(mM2s.length, event.values.length);
+
+        mCount++;
+
+        for (int i = 0; i < event.values.length; i++) {
+            float delta = event.values[i] - mMeans[i];
+            mMeans[i] += delta / mCount;
+            mM2s[i] += delta * (event.values[i] - mMeans[i]);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{1.0f, 1.0f, 1.0f});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new float[]{0.5f, 0.5f, 0.5f});
+        // Sensors that we don't want to test at this time but still want to record the values.
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_ORIENTATION,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_PRESSURE,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GRAVITY,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE});
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
new file mode 100644
index 0000000..0e5fb90
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link StandardDeviationVerification}.
+ */
+public class StandardDeviationVerificationTest extends TestCase {
+
+    /**
+     * Test {@link StandardDeviationVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        // Stddev should be {sqrt(2.5), sqrt(2.5), sqrt(2.5)}
+        float[][] values = {
+                {0, 1, 0},
+                {1, 2, 2},
+                {2, 3, 4},
+                {3, 4, 6},
+                {4, 5, 8},
+        };
+        float[] standardDeviations = {
+                (float) Math.sqrt(2.5), (float) Math.sqrt(2.5), (float) Math.sqrt(10.0)
+        };
+
+        float[] threshold = {2, 2, 4};
+        runVerification(threshold, values, true, standardDeviations);
+
+        threshold = new float[]{1, 2, 4};
+        runVerification(threshold, values, false, standardDeviations);
+
+        threshold = new float[]{2, 1, 4};
+        runVerification(threshold, values, false, standardDeviations);
+
+        threshold = new float[]{2, 2, 3};
+        runVerification(threshold, values, false, standardDeviations);
+    }
+
+    private void runVerification(float[] threshold, float[][] values, boolean pass,
+            float[] standardDeviations) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(threshold, values);
+        if (pass) {
+            verification.verify(stats);
+        } else {
+            try {
+                verification.verify(stats);
+                fail("Expected an AssertionError");
+            } catch (AssertionError e) {
+                // Expected;
+            }
+        }
+        assertEquals(pass, stats.getValue(StandardDeviationVerification.PASSED_KEY));
+        float[] actual = (float[]) stats.getValue(SensorStats.STANDARD_DEVIATION_KEY);
+        for (int i = 0; i < standardDeviations.length; i++) {
+            assertEquals(standardDeviations[i], actual[i], 0.1);
+        }
+    }
+
+    private ISensorVerification getVerification(float[] threshold, float[] ... values) {
+        ISensorVerification verification = new StandardDeviationVerification(threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+}