Merge "CTS:Start a GL activity with a unreachable stopping criteria"
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 bcd00ed..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.sensorTestOperations.VerifyMeasurementsOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.MeanVerification;
 
 import java.util.concurrent.TimeUnit;
 
@@ -72,22 +73,24 @@
      * - the values representing the expectation of the test
      * - the mean of values sampled from the sensor
      */
-    private void verifyMeasurements(double ... expectations) throws Throwable {
+    private void verifyMeasurements(float ... expectations) throws Throwable {
         Thread.sleep(500 /*ms*/);
-        VerifyMeasurementsOperation verifyMeasurements = new VerifyMeasurementsOperation(
+        TestSensorOperation verifyMeasurements = new TestSensorOperation(
                 getApplicationContext(),
                 Sensor.TYPE_ACCELEROMETER,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        verifyMeasurements.addVerification(new MeanVerification(
                 expectations,
-                1.95f /* m / s^2 */);
+                new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
         verifyMeasurements.execute();
         logSuccess();
     }
 
     private void delayedVerifyMeasurements(
             String message,
-            double ... expectations) throws Throwable {
+            float ... expectations) throws Throwable {
         appendText(String.format("\n%s.", message));
         appendText("A sound will be played once the verification is complete...");
         waitForUser();
@@ -100,7 +103,7 @@
         }
     }
 
-    private void verifyMeasurements(String message, double ... expectations) throws Throwable {
+    private void verifyMeasurements(String message, float ... expectations) throws Throwable {
         appendText(String.format("\n%s.", message));
         appendText("Press 'Next' when ready and keep the device steady.");
         waitForUser();
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 df15b66..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.sensorTestOperations.VerifySignumOperation;
+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.
@@ -89,19 +90,22 @@
     private void verifyMeasurements(
             String scenarioInstructions,
             boolean usePortraitOrientation,
-            double ... expectations) throws Throwable {
+            int ... expectations) throws Throwable {
         final String orientation = usePortraitOrientation ? "Portrait": "Landscape";
         appendText(String.format("\n[Device orientation]: %s", orientation));
         appendText(String.format("Press 'Next' and %s.", scenarioInstructions));
         waitForUser();
 
         Thread.sleep(500 /*ms*/);
-        VerifySignumOperation verifySignum = new VerifySignumOperation(
+        TestSensorOperation verifySignum = new TestSensorOperation(
                 getApplicationContext(),
                 Sensor.TYPE_GYROSCOPE,
                 SensorManager.SENSOR_DELAY_FASTEST,
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        verifySignum.addVerification(new SigNumVerification(
                 expectations,
-                0.2 /*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 713710d..a131b2b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -19,10 +19,13 @@
 import android.graphics.Color;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyNormOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyStandardDeviationOperation;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+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.
@@ -43,27 +46,34 @@
     }
 
     private void calibrateMagnetometer() {
-        SensorManagerTestVerifier magnetometer = new SensorManagerTestVerifier(
-                this.getApplicationContext(),
-                Sensor.TYPE_MAGNETIC_FIELD,
-                SensorManager.SENSOR_DELAY_NORMAL,
-                0 /*reportLatencyInUs*/) {
+        SensorEventListener2 listener = new SensorEventListener2() {
             @Override
             public void onSensorChanged(SensorEvent event) {
                 float values[] = event.values;
                 clearText();
-                appendText(
-                        "Please calibrate the Magnetometer by moving it in 8 shapes in different " +
-                                "orientations.");
-                appendText(
-                        String.format("->  (%.2f, %.2f, %.2f) uT", values[0], values[1], values[2]),
-                        Color.GRAY);
+                appendText("Please calibrate the Magnetometer by moving it in 8 shapes in "
+                        + "different orientations.");
+                appendText(String.format("->  (%.2f, %.2f, %.2f) uT", values[0], values[1],
+                        values[2]), Color.GRAY);
                 appendText("Then leave the device in a flat surface and press Next...\n");
             }
+
+            @Override
+            public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+            @Override
+            public void onFlushCompleted(Sensor sensor) {}
         };
-        magnetometer.registerListener();
-        waitForUser();
-        magnetometer.unregisterListener();
+
+        TestSensorManager magnetometer = new TestSensorManager(
+                this.getApplicationContext(), Sensor.TYPE_MAGNETIC_FIELD,
+                SensorManager.SENSOR_DELAY_NORMAL, 0);
+        try {
+            magnetometer.registerListener(new TestSensorEventListener(listener));
+            waitForUser();
+        } finally {
+            magnetometer.unregisterListener();
+        }
     }
 
     /**
@@ -91,12 +101,15 @@
                 (SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
         float magneticFieldEarthThreshold =
                 expectedMagneticFieldEarth - SensorManager.MAGNETIC_FIELD_EARTH_MIN;
-        VerifyNormOperation verifyNorm = new VerifyNormOperation(
+        TestSensorOperation verifyNorm = new TestSensorOperation(
                 this.getApplicationContext(),
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST,
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        verifyNorm.addVerification(new MagnitudeVerification(
                 expectedMagneticFieldEarth,
-                magneticFieldEarthThreshold);
+                magneticFieldEarthThreshold));
         verifyNorm.execute();
         logSuccess();
     }
@@ -125,12 +138,14 @@
      * the failure to help track down the issue.
      */
     private void verifyStandardDeviation() throws Throwable {
-        VerifyStandardDeviationOperation verifyStdDev = new VerifyStandardDeviationOperation(
+        TestSensorOperation verifyStdDev = new TestSensorOperation(
                 this.getApplicationContext(),
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
-                2f /* uT */);
+                100 /* event count */);
+        verifyStdDev.addVerification(new StandardDeviationVerification(
+                new float[]{2f, 2f, 2f} /* uT */));
         verifyStdDev.execute();
         logSuccess();
     }
diff --git a/apps/cts-usb-accessory/Android.mk b/apps/cts-usb-accessory/Android.mk
index 76022a1..8d18da3 100644
--- a/apps/cts-usb-accessory/Android.mk
+++ b/apps/cts-usb-accessory/Android.mk
@@ -28,7 +28,7 @@
 
 LOCAL_C_INCLUDES += \
 	bionic/libc/kernel/uapi \
-	bionic/libc/kernel/uapi/asm-$(HOST_ARCH) \
+	bionic/libc/kernel/uapi/asm-x86 \
 
 LOCAL_STATIC_LIBRARIES := libusbhost libcutils
 LOCAL_LDLIBS += -lpthread
diff --git a/libs/deviceutil/src/android/cts/util/DeviceReportLog.java b/libs/deviceutil/src/android/cts/util/DeviceReportLog.java
index b707fc8..a3ceecf 100644
--- a/libs/deviceutil/src/android/cts/util/DeviceReportLog.java
+++ b/libs/deviceutil/src/android/cts/util/DeviceReportLog.java
@@ -16,19 +16,24 @@
 
 package android.cts.util;
 
-import com.android.cts.util.ReportLog;
-
 import android.app.Instrumentation;
 import android.os.Bundle;
 import android.util.Log;
 
+import com.android.cts.util.ReportLog;
+
 public class DeviceReportLog extends ReportLog {
     private static final String TAG = "DeviceCtsReport";
     private static final String CTS_RESULT = "CTS_RESULT";
     private static final int INST_STATUS_IN_PROGRESS = 2;
+    private static final int BASE_DEPTH = 4;
 
-    DeviceReportLog() {
-        mDepth = 4;
+    public DeviceReportLog() {
+        mDepth = BASE_DEPTH;
+    }
+
+    public DeviceReportLog(int depth) {
+        mDepth = BASE_DEPTH + depth;
     }
 
     @Override
diff --git a/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp b/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
index fe91cb3..06c92d6 100644
--- a/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
+++ b/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
@@ -42,12 +42,12 @@
     : mHwId(hwId),
       mPcmHandle(NULL)
 {
-    LOGV("AudioPlaybackLocal %x", (unsigned int)this);
+    LOGV("AudioPlaybackLocal %x", (unsigned long)this);
 }
 
 AudioPlaybackLocal::~AudioPlaybackLocal()
 {
-    LOGV("~AudioPlaybackLocal %x", (unsigned int)this);
+    LOGV("~AudioPlaybackLocal %x", (unsigned long)this);
     releaseHw();
 }
 
@@ -108,7 +108,7 @@
 void AudioPlaybackLocal::releaseHw()
 {
     if (mPcmHandle != NULL) {
-        LOGV("releaseHw %x", (unsigned int)this);
+        LOGV("releaseHw %x", (unsigned long)this);
         doStop();
         pcm_close(mPcmHandle);
         mPcmHandle = NULL;
diff --git a/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp b/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
index 1325949..eda705d 100644
--- a/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
+++ b/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
@@ -29,12 +29,12 @@
     : mHwId(hwId),
       mPcmHandle(NULL)
 {
-    LOGV("AudioRecordingLocal %x", (unsigned int)this);
+    LOGV("AudioRecordingLocal %x", (unsigned long)this);
 }
 
 AudioRecordingLocal::~AudioRecordingLocal()
 {
-    LOGV("~AudioRecordingLocal %x", (unsigned int)this);
+    LOGV("~AudioRecordingLocal %x", (unsigned long)this);
     releaseHw();
 }
 
@@ -97,7 +97,7 @@
 void AudioRecordingLocal::releaseHw()
 {
     if (mPcmHandle != NULL) {
-        LOGV("releaseHw %x", (unsigned int)this);
+        LOGV("releaseHw %x", (unsigned long)this);
         doStop();
         pcm_close(mPcmHandle);
         mPcmHandle = NULL;
diff --git a/suite/audio_quality/lib/src/task/TaskProcess.cpp b/suite/audio_quality/lib/src/task/TaskProcess.cpp
index f1e47af..061dda5 100644
--- a/suite/audio_quality/lib/src/task/TaskProcess.cpp
+++ b/suite/audio_quality/lib/src/task/TaskProcess.cpp
@@ -271,7 +271,7 @@
             list.push_back(param);
             LOGD(" val %s", param.getParamString().string());
         } else if (isInput && (StringUtil::compare(item[0], "consti") == 0)) {
-            long long value = atoll(item[1].string());
+            int64_t value = atoll(item[1].string());
             TaskCase::Value v(value);
             Param param(v);
             list.push_back(param);
diff --git a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
index d992839..83c339b 100644
--- a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
+++ b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
@@ -201,14 +201,16 @@
 
     // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
     static class TestEnvironment {
-        private Locale mDefaultLocale;
-        private String mUserHome;
-        private String mJavaIoTmpDir;
-        private HostnameVerifier mHostnameVerifier;
-        private SSLSocketFactory mSslSocketFactory;
+        private final Locale mDefaultLocale;
+        private final TimeZone mDefaultTimeZone;
+        private final String mUserHome;
+        private final String mJavaIoTmpDir;
+        private final HostnameVerifier mHostnameVerifier;
+        private final SSLSocketFactory mSslSocketFactory;
 
         TestEnvironment() {
             mDefaultLocale = Locale.getDefault();
+            mDefaultTimeZone = TimeZone.getDefault();
             mUserHome = System.getProperty("user.home");
             mJavaIoTmpDir = System.getProperty("java.io.tmpdir");
             mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
@@ -217,6 +219,7 @@
 
         void reset() {
             Locale.setDefault(mDefaultLocale);
+            TimeZone.setDefault(mDefaultTimeZone);
             System.setProperty("user.home", mUserHome);
             System.setProperty("java.io.tmpdir", mJavaIoTmpDir);
             Authenticator.setDefault(null);
diff --git a/tests/jni/Android.mk b/tests/jni/Android.mk
index 28aa15a..139118d 100644
--- a/tests/jni/Android.mk
+++ b/tests/jni/Android.mk
@@ -30,7 +30,7 @@
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog
+LOCAL_SHARED_LIBRARIES := libnativehelper liblog libdl
 
 LOCAL_SRC_FILES += android_os_cts_CpuFeatures.cpp
 LOCAL_C_INCLUDES += ndk/sources/cpufeatures
diff --git a/tests/src/android/renderscript/cts/intrinsic_3dlut.rs b/tests/src/android/renderscript/cts/intrinsic_3dlut.rs
new file mode 100644
index 0000000..d577b63
--- /dev/null
+++ b/tests/src/android/renderscript/cts/intrinsic_3dlut.rs
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#include "shared.rsh"
+
+static rs_allocation gCube;
+static int4 gDims;
+static float4 gCoordMul;
+
+void setCube(rs_allocation c) {
+    gCube = c;
+    gDims.x = rsAllocationGetDimX(gCube);
+    gDims.y = rsAllocationGetDimY(gCube);
+    gDims.z = rsAllocationGetDimZ(gCube);
+    gDims.w = 0;
+    gCoordMul = (float4)(1.f / 255.f) * convert_float4(gDims - 1);
+}
+
+uchar4 __attribute__((kernel)) root(uchar4 in) {
+    float4 baseCoord = convert_float4(in) * gCoordMul;
+    int4 coord1 = convert_int4(floor(baseCoord));
+    int4 coord2 = min(coord1 + 1, gDims - 1);
+    float4 f = baseCoord - convert_float4(coord1);
+
+    float4 v000 = convert_float4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord1.z));
+    float4 v100 = convert_float4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord1.z));
+    float4 v010 = convert_float4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord1.z));
+    float4 v110 = convert_float4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord1.z));
+    float4 v001 = convert_float4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord2.z));
+    float4 v101 = convert_float4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord2.z));
+    float4 v011 = convert_float4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord2.z));
+    float4 v111 = convert_float4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord2.z));
+
+    float4 yz00 = mix(v000, v100, f.x);
+    float4 yz10 = mix(v010, v110, f.x);
+    float4 yz01 = mix(v001, v101, f.x);
+    float4 yz11 = mix(v011, v111, f.x);
+
+    float4 z0 = mix(yz00, yz10, f.y);
+    float4 z1 = mix(yz01, yz11, f.y);
+
+    float4 v = mix(z0, z1, f.z);
+
+    v = clamp(v, 0.f, 255.f);
+    uchar4 o = convert_uchar4(v + 0.5f);
+    o.w = in.w;
+    return o;
+}
+
diff --git a/tests/src/android/renderscript/cts/intrinsic_blur.rs b/tests/src/android/renderscript/cts/intrinsic_blur.rs
new file mode 100644
index 0000000..88f9ca5
--- /dev/null
+++ b/tests/src/android/renderscript/cts/intrinsic_blur.rs
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#include "shared.rsh"
+
+int height;
+int width;
+static int radius;
+rs_allocation ScratchPixel1;
+rs_allocation ScratchPixel2;
+
+const int MAX_RADIUS = 25;
+
+// Store our coefficients here
+static float gaussian[MAX_RADIUS * 2 + 1];
+
+float4 __attribute__((kernel)) convert1_uToF(uchar v) {
+    float4 r = rsUnpackColor8888(v);
+    return r.r;
+}
+
+float4 __attribute__((kernel)) convert4_uToF(uchar4 v) {
+    return rsUnpackColor8888(v);
+}
+
+uchar __attribute__((kernel)) convert1_fToU(float4 v) {
+    uchar4 r = rsPackColorTo8888(v);
+    return r.r;
+}
+
+uchar4 __attribute__((kernel)) convert4_fToU(float4 v) {
+    return rsPackColorTo8888(v);
+}
+
+void setRadius(int rad) {
+    // This function is a duplicate of:
+    // RsdCpuScriptIntrinsicBlur::ComputeGaussianWeights()
+    // Which is the reference C implementation
+    radius = rad;
+    const float e = M_E;
+    const float pi = M_PI;
+    float sigma = 0.4f * (float)radius + 0.6f;
+    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+    float normalizeFactor = 0.0f;
+    float floatR = 0.0f;
+    for (int r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+        normalizeFactor += gaussian[r + radius];
+    }
+
+    normalizeFactor = 1.0f / normalizeFactor;
+    for (int r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] *= normalizeFactor;
+    }
+}
+
+float4 __attribute__((kernel)) vert(uint32_t x, uint32_t y) {
+    float4 blurredPixel = 0;
+    int gi = 0;
+    for (int r = -radius; r <= radius; r ++) {
+        int validH = rsClamp((int)y + r, (int)0, (int)(height - 1));
+        float4 i = rsGetElementAt_float4(ScratchPixel2, x, validH);
+        blurredPixel += i * gaussian[gi++];
+    }
+    return blurredPixel;
+}
+
+float4 __attribute__((kernel)) horz(uint32_t x, uint32_t y) {
+    float4 blurredPixel = 0;
+    int gi = 0;
+    for (int r = -radius; r <= radius; r ++) {
+        // Stepping left and right away from the pixel
+        int validX = rsClamp((int)x + r, (int)0, (int)(width - 1));
+        float4 i = rsGetElementAt_float4(ScratchPixel1, validX, y);
+        blurredPixel += i * gaussian[gi++];
+    }
+    return blurredPixel;
+}
+
+
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index e0ad6e5..2ad58ba 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -34,7 +34,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner mockito-target android-ex-camera2
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner mockito-target android-ex-camera2
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java
deleted file mode 100644
index 116ac80..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-
-import android.hardware.cts.helpers.SensorTestCase;
-
-import android.hardware.cts.helpers.sensorTestOperations.VerifyEventOrderingOperation;
-
-/**
- * Verifies the proper ordering in time of sensor events.
- */
-public class SensorEventOrderingTests extends SensorTestCase {
-    /**
-     * Builder for the test suite.
-     * This is the method that will build dynamically the set of test cases to execute.
-     * Each 'base' test case is composed by three parts:
-     * - the matrix definition
-     * - the test method that will execute the test case
-     * - a static method that will combine both and add test case instances to the test suite
-     */
-    public static Test suite() {
-        TestSuite testSuite = new TestSuite();
-
-        // add test generation routines
-        createEventOrderingTestCases(testSuite);
-
-        return testSuite;
-    }
-
-    /**
-     * Event ordering test cases.
-     */
-    private int mSensorType;
-    private int mSamplingRateInUs;
-    private int mReportLatencyInUs;
-
-    private static void createEventOrderingTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, SamplingRateInUs, ReportLatencyInUs },
-                { Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_LINEAR_ACCELERATION, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_RELATIVE_HUMIDITY, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_AMBIENT_TEMPERATURE, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,0 },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorEventOrderingTests test = new SensorEventOrderingTests();
-            test.mSensorType = definition[0];
-            test.mSamplingRateInUs = definition[1];
-            test.mReportLatencyInUs = definition[2];
-            test.setName("testEventOrdering");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies the ordering of the sampled data reported by the Sensor under test.
-     * This test is used to guarantee that sensor data is reported in the order it occurs, and that
-     * events are always reported in order.
-     *
-     * The test takes a set of samples from the Sensor under test, and then it verifies that each
-     * event's timestamp is in the future compared with the previous event. At the end of the
-     * validation, the full set of events is verified to be ordered by timestamp as they are
-     * generated.
-     *
-     * The test can be susceptible to errors if the sensor sampled data is not timestamped at the
-     * Hardware level. Or events sampled at high rates are added to the FIFO without controlling the
-     * appropriate ordering of the events.
-     *
-     * The assertion associated with the test provides the information of the two consecutive events
-     * that cause the test to fail.
-     */
-    public void testEventOrdering() throws Throwable {
-        VerifyEventOrderingOperation operation = new VerifyEventOrderingOperation(
-                this.getContext(),
-                mSensorType,
-                mSamplingRateInUs,
-                mReportLatencyInUs);
-        operation.execute();
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java
deleted file mode 100644
index b35f515..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.hardware.Sensor;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorTestCase;
-
-import android.hardware.cts.helpers.sensorTestOperations.VerifyJitteringOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyMaximumFrequencyOperation;
-
-/**
- * Verifies several properties of the sampling rate of the different sensors in the platform.
- */
-public class SensorFrequencyTests extends SensorTestCase {
-    private int mSensorType;
-    private int mReportLatencyInUs;
-    private int mThresholdPercentageOfNs;
-
-    /**
-     * Builder for the test suite.
-     * This is the method that will build dynamically the set of test cases to execute.
-     * Each 'base' test case is composed by three parts:
-     * - the matrix definition
-     * - the test method that will execute the test case
-     * - a static method that will combine both and add test case instances to the test suite
-     */
-    public static Test suite() {
-        TestSuite testSuite = new TestSuite();
-
-        // add test generation routines
-        createMaxFrequencyExpectedTestCases(testSuite);
-        // TODO: tests are a unreliable in the lab
-        //createMaxFrequencyTestCases(testSuite);
-        //createJitteringTestCases(testSuite);
-
-        return testSuite;
-    }
-
-    /**
-     * Max frequency test cases.
-     */
-    private static void createMaxFrequencyTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, ReportLatencyInUs, ThresholdPercentageOfNs },
-                { Sensor.TYPE_ACCELEROMETER, 0, 10 },
-                { Sensor.TYPE_GYROSCOPE, 0, 10 },
-                { Sensor.TYPE_MAGNETIC_FIELD, 0, 10 },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorFrequencyTests test = new SensorFrequencyTests();
-            test.mSensorType = definition[0];
-            test.mReportLatencyInUs = definition[1];
-            test.mThresholdPercentageOfNs = definition[2];
-            test.setName("testMaxFrequency");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the Sensor under test can sample and report data at the Maximum
-     * frequency (sampling rate) it advertises.
-     *
-     * The test takes a set of samples from the sensor under test, and calculates the mean of the
-     * frequency at which the events are reported. The frequency between events is calculated by
-     * looking at the delta between the timestamps associated with each event.
-     *
-     * The test is susceptible to errors if the Sensor is not capable to sample data at the maximum
-     * rate it supports, or the sensor events are not timestamped at the Hardware level.
-     *
-     * The assertion associated with the test provides the required data to identify:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expected frequency
-     * - the observed frequency
-     * In addition to that, the device's debug output (adb logcat) dumps the set of timestamp deltas
-     * associated with the set of data gathered from the Sensor under test.
-     */
-    public void testMaxFrequency() throws Throwable {
-        VerifyMaximumFrequencyOperation operation = new VerifyMaximumFrequencyOperation(
-                this.getContext(),
-                mSensorType,
-                mReportLatencyInUs,
-                mThresholdPercentageOfNs);
-        operation.execute();
-    }
-
-    /**
-     * Jittering test cases.
-     */
-    private static void createJitteringTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, ReportLatencyInUs, ThresholdPercentageOfNs },
-                { Sensor.TYPE_ACCELEROMETER, 0, 10 },
-                { Sensor.TYPE_GYROSCOPE, 0, 10 },
-                { Sensor.TYPE_MAGNETIC_FIELD, 0, 10 },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorFrequencyTests test = new SensorFrequencyTests();
-            test.mSensorType = definition[0];
-            test.mReportLatencyInUs = definition[1];
-            test.mThresholdPercentageOfNs = definition[2];
-            test.setName("testJittering");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the event jittering associated with the sampled data reported by the
-     * Sensor under test, aligns with the requirements imposed in the CDD.
-     * This test characterizes how the sensor behaves while sampling data at a specific rate.
-     *
-     * The test takes a set of samples from the sensor under test, using the maximum sampling rate
-     * advertised by the Sensor under test. It then compares the 95%ile associated with the
-     * jittering of the timestamps, with an expected value.
-     *
-     * The test is susceptible to errors if the sensor events are not timestamped at the Hardware
-     * level.
-     *
-     * The assertion associated with the failure provides the following information:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expectation of the test with respect of the 95%ile
-     * - the calculated 95%ile jittering
-     * Additionally, the device's debug output (adb logcat) dumps the set of jitter values
-     * calculated.
-     */
-    public void testJittering() throws Throwable {
-        VerifyJitteringOperation operation = new VerifyJitteringOperation(
-                this.getContext(),
-                mSensorType,
-                mReportLatencyInUs,
-                mThresholdPercentageOfNs);
-        operation.execute();
-    }
-
-    /**
-     * Max Frequency expected Test Cases.
-     */
-    private int mExpectedSamplingRateInUs;
-
-    private static void createMaxFrequencyExpectedTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, ExpectedSamplingRateInUs },
-                { Sensor.TYPE_ACCELEROMETER, 10000 /* 100 Hz */ },
-                { Sensor.TYPE_GYROSCOPE, 10000 /* 100 Hz */ },
-                { Sensor.TYPE_MAGNETIC_FIELD, 100000 /* 10 Hz */ },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorFrequencyTests test = new SensorFrequencyTests();
-            test.mSensorType = definition[0];
-            test.mExpectedSamplingRateInUs = definition[1];
-            test.setName("testMaxFrequencyExpected");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the sensor's maximum advertised frequency (sampling rate) complies
-     * with the required frequency set in the CDD.
-     * This characterizes that the sensor is able to provide data at the rate the platform requires
-     * it.
-     *
-     * The test simply compares the sampling rate specified in the CDD with the maximum sampling
-     * rate advertised by the Sensor under test.
-     *
-     * The test can fail if the Sensor Hardware does not support the sampling rate required by the
-     * platform.
-     *
-     * The assertion associated with the test failure contains:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expected maximum sampling rate
-     * - the observed maximum sampling rate
-     */
-    public void testMaxFrequencyExpected() {
-        Sensor sensor = SensorCtsHelper.getSensor(this.getContext(), mSensorType);
-        int samplingRateInUs = sensor.getMinDelay();
-        String message = String.format(
-                "samplingRateInUs| expected:%d, actual:%d",
-                mExpectedSamplingRateInUs,
-                samplingRateInUs);
-        assertTrue(message, mExpectedSamplingRateInUs >= samplingRateInUs);
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index a1aa760..d5d7972 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -15,23 +15,19 @@
  */
 package android.hardware.cts;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
 import android.content.Context;
-
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-
 import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorTestCase;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
+import android.hardware.cts.helpers.SensorStats;
+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.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
 
-import android.hardware.cts.helpers.sensorTestOperations.ParallelCompositeSensorTestOperation;
-import android.hardware.cts.helpers.sensorTestOperations.RepeatingSensorTestOperation;
-import android.hardware.cts.helpers.sensorTestOperations.SequentialCompositeSensorTestOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyEventOrderingOperation;
+import junit.framework.Test;
+import junit.framework.TestSuite;
 
 import java.util.Random;
 
@@ -43,6 +39,8 @@
  *          -w com.android.cts.hardware/android.test.InstrumentationCtsTestRunner
  */
 public class SensorIntegrationTests extends SensorTestCase {
+    private static final String TAG = "SensorIntegrationTests";
+
     /**
      * Builder for the test suite.
      * This is the method that will build dynamically the set of test cases to execute.
@@ -87,7 +85,7 @@
      */
     public void testSensorsWithSeveralClients() throws Throwable {
         final int ITERATIONS = 50;
-        final int BATCHING_RATE_IN_SECONDS = 5;
+        final int MAX_REPORTING_LATENCY_IN_SECONDS = 5;
         final Context context = this.getContext();
 
         int sensorTypes[] = {
@@ -95,23 +93,28 @@
                 Sensor.TYPE_MAGNETIC_FIELD,
                 Sensor.TYPE_GYROSCOPE };
 
-        ParallelCompositeSensorTestOperation operation = new ParallelCompositeSensorTestOperation();
+        ParallelSensorOperation operation = new ParallelSensorOperation();
         for(int sensorType : sensorTypes) {
-            SensorTestOperation continuousOperation = new VerifyEventOrderingOperation(
+            TestSensorOperation continuousOperation = new TestSensorOperation(
                     context,
                     sensorType,
                     SensorManager.SENSOR_DELAY_NORMAL,
-                    0 /* reportLatencyInUs */);
-            operation.add(new RepeatingSensorTestOperation(continuousOperation, ITERATIONS));
+                    0 /* reportLatencyInUs */,
+                    100 /* event count */);
+            continuousOperation.addVerification(new EventOrderingVerification());
+            operation.add(new RepeatingSensorOperation(continuousOperation, ITERATIONS));
 
-            SensorTestOperation batchingOperation = new VerifyEventOrderingOperation(
+            TestSensorOperation batchingOperation = new TestSensorOperation(
                     context,
                     sensorType,
-                    SensorTestInformation.getMaxSamplingRateInUs(context, sensorType),
-                    SensorCtsHelper.getSecondsAsMicroSeconds(BATCHING_RATE_IN_SECONDS));
-            operation.add(new RepeatingSensorTestOperation(batchingOperation, ITERATIONS));
+                    SensorCtsHelper.getSensor(getContext(), sensorType).getMinDelay(),
+                    SensorCtsHelper.getSecondsAsMicroSeconds(MAX_REPORTING_LATENCY_IN_SECONDS),
+                    100);
+            batchingOperation.addVerification(new EventOrderingVerification());
+            operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
         }
         operation.execute();
+        SensorStats.logStats(TAG, operation.getStats());
     }
 
     /**
@@ -140,7 +143,7 @@
         final int INSTANCES_TO_USE = 5;
         final int ITERATIONS_TO_EXECUTE = 100;
 
-        ParallelCompositeSensorTestOperation operation = new ParallelCompositeSensorTestOperation();
+        ParallelSensorOperation operation = new ParallelSensorOperation();
         int sensorTypes[] = {
                 Sensor.TYPE_ACCELEROMETER,
                 Sensor.TYPE_MAGNETIC_FIELD,
@@ -148,14 +151,15 @@
 
         for(int sensorType : sensorTypes) {
             for(int instance = 0; instance < INSTANCES_TO_USE; ++instance) {
-                SequentialCompositeSensorTestOperation sequentialOperation =
-                        new SequentialCompositeSensorTestOperation();
+                SequentialSensorOperation sequentialOperation = new SequentialSensorOperation();
                 for(int iteration = 0; iteration < ITERATIONS_TO_EXECUTE; ++iteration) {
-                    VerifyEventOrderingOperation sensorOperation = new VerifyEventOrderingOperation(
+                    TestSensorOperation sensorOperation = new TestSensorOperation(
                             this.getContext(),
                             sensorType,
                             this.generateSamplingRateInUs(sensorType),
-                            this.generateReportLatencyInUs());
+                            this.generateReportLatencyInUs(),
+                            100);
+                    sensorOperation.addVerification(new EventOrderingVerification());
                     sequentialOperation.add(sensorOperation);
                 }
                 operation.add(sequentialOperation);
@@ -163,6 +167,7 @@
         }
 
         operation.execute();
+        SensorStats.logStats(TAG, operation.getStats());
     }
 
     /**
@@ -212,24 +217,30 @@
     public void testSensorStoppingInteraction() throws Throwable {
         Context context = this.getContext();
 
-        SensorTestOperation tester = new VerifyEventOrderingOperation(
+        TestSensorOperation tester = new TestSensorOperation(
                 context,
                 mSensorTypeTester,
                 SensorManager.SENSOR_DELAY_NORMAL,
-                0 /*reportLatencyInUs*/);
-        tester.start();
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        tester.addVerification(new EventOrderingVerification());
 
-        SensorTestOperation testee = new VerifyEventOrderingOperation(
+        TestSensorOperation testee = new TestSensorOperation(
                 context,
                 mSensorTypeTestee,
                 SensorManager.SENSOR_DELAY_UI,
-                0 /*reportLatencyInUs*/);
-        testee.start();
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        testee.addVerification(new EventOrderingVerification());
 
-        testee.waitForCompletion();
-        tester.waitForCompletion();
+        ParallelSensorOperation operation = new ParallelSensorOperation();
+        operation.add(tester, testee);
+        operation.execute();
+        SensorStats.logStats(TAG, operation.getStats());
 
+        testee = testee.clone();
         testee.execute();
+        SensorStats.logStats(TAG, testee.getStats());
     }
 
     /**
@@ -254,9 +265,8 @@
                 break;
             case 4:
             default:
-                int maxSamplingRate = SensorTestInformation.getMaxSamplingRateInUs(
-                        this.getContext(),
-                        sensorType);
+                int maxSamplingRate = SensorCtsHelper.getSensor(getContext(), sensorType)
+                        .getMinDelay();
                 rate = maxSamplingRate * mGenerator.nextInt(10);
         }
         return rate;
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java
deleted file mode 100644
index ea025ba..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-
-import android.hardware.cts.helpers.SensorTestCase;
-
-import android.hardware.cts.helpers.sensorTestOperations.VerifyNormOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyStandardDeviationOperation;
-
-/**
- * Verifies several properties of the sensor measurements.
- */
-public class SensorMeasurementTests extends SensorTestCase {
-    private int mSensorType;
-    private int mSamplingRateInUs;
-
-    /**
-     * Builder for the test suite.
-     * This is the method that will build dynamically the set of test cases to execute.
-     * Each 'base' test case is composed by three parts:
-     * - the matrix definition
-     * - the test method that will execute the test case
-     * - a static method that will combine both and add test case instances to the test suite
-     */
-    public static Test suite() {
-        TestSuite testSuite = new TestSuite();
-
-        // add test generation routines
-        createEventNormTestCases(testSuite);
-        createStandardDeviationTestCases(testSuite);
-
-        return testSuite;
-    }
-
-    /**
-     * SensorEvent Norm test cases.
-     *
-     * Regress:
-     * - b/9503957
-     * - b/9611609
-     */
-    private float mReferenceValue;
-    private float mThreshold;
-
-    private static void createEventNormTestCases(TestSuite testSuite) {
-        Object testDefinitionMatrix[][] = {
-                // { SensorType, SamplingRateInUs, ReferenceValue, Threshold },
-                { Sensor.TYPE_ACCELEROMETER,
-                        SensorManager.SENSOR_DELAY_FASTEST,
-                        SensorManager.STANDARD_GRAVITY,
-                        1.5f /* m / s^2 */},
-                { Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, 0.0f, 2.5f /* dps */ },
-        };
-
-        for(Object definition[] : testDefinitionMatrix)  {
-            SensorMeasurementTests test = new SensorMeasurementTests();
-            test.mSensorType = (Integer)definition[0];
-            test.mSamplingRateInUs = (Integer)definition[1];
-            test.mReferenceValue = (Float)definition[2];
-            test.mThreshold = (Float)definition[3];
-            test.setName("testEventNorm");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the Norm of the sensor data is close to the expected reference value.
-     * The units of the reference value are dependent on the type of sensor.
-     * This test is used to verify that the data reported by the sensor is close to the expected
-     * range and scale.
-     *
-     * The test takes a sample from the sensor under test and calculates the Euclidean Norm of the
-     * vector represented by the sampled data. It then compares it against the test expectations
-     * that are represented by a reference value and a threshold.
-     *
-     * The test is susceptible to errors when the Sensor under test is uncalibrated, or the units in
-     * which the data are reported and the expectations are set are different.
-     *
-     * The assertion associated with the test provides the required data needed to identify any
-     * possible issue. It provides:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the values representing the expectation of the test
-     * - the values sampled from the sensor
-     */
-    public void testEventNorm() throws Throwable {
-        VerifyNormOperation operation = new VerifyNormOperation(
-                this.getContext(),
-                mSensorType,
-                mSamplingRateInUs,
-                mReferenceValue,
-                mThreshold);
-        operation.execute();
-    }
-
-    /**
-     * SensorEvent Standard Deviation test cases.
-     */
-    private int mReportLatencyInUs;
-    private float mExpectedStandardDeviation;
-
-    private static void createStandardDeviationTestCases(TestSuite testSuite) {
-        Object testDefinitionMatrix[][] = {
-                // { SensorType, SamplingRateInUs, ReportLatencyInUs, ExpectedStandardDeviation },
-                { Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, 0, 1f /* m/s^2 */ },
-                { Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, 0, 0.5f /* dps */ },
-        };
-
-        for(Object definition[] : testDefinitionMatrix) {
-            SensorMeasurementTests test = new SensorMeasurementTests();
-            test.mSensorType = (Integer)definition[0];
-            test.mSamplingRateInUs = (Integer)definition[1];
-            test.mReportLatencyInUs = (Integer)definition[2];
-            test.mExpectedStandardDeviation = (Float)definition[3];
-            test.setName("testStandardDeviation");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the standard deviation of a set of sampled data from a particular
-     * sensor falls into the expectations defined in the CDD. The verification applies to each axis
-     * of the sampled data reported by the Sensor under test.
-     * This test is used to validate the requirement imposed by the CDD to Sensors in Android. And
-     * characterizes how the Sensor behaves while static.
-     *
-     * The test takes a set of samples from the sensor under test, and calculates the Standard
-     * Deviation for each of the axes the Sensor reports data for. The StdDev is compared against
-     * the expected value documented in the CDD.
-     *
-     * The test is susceptible to errors if the device is moving while the test is running, or if
-     * the Sensor's sampled data indeed falls into a large StdDev.
-     *
-     * The assertion associated with the test provides the required data to identify any possible
-     * issue. It provides:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expectation of the test
-     * - the std dev calculated and the axis it applies to
-     * Additionally, the device's debug output (adb logcat) dumps the set of values associated with
-     * the failure to help track down the issue.
-     */
-    public void testStandardDeviation() throws Throwable {
-        VerifyStandardDeviationOperation operation = new VerifyStandardDeviationOperation(
-                this.getContext(),
-                mSensorType,
-                mSamplingRateInUs,
-                mReportLatencyInUs,
-                mExpectedStandardDeviation);
-        operation.execute();
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
new file mode 100644
index 0000000..222da56
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts;
+
+import android.app.Instrumentation;
+import android.cts.util.CtsAndroidTestCase;
+import android.cts.util.DeviceReportLog;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.SensorStats;
+import android.util.Log;
+
+import com.android.cts.util.ReportLog;
+import com.android.cts.util.ResultType;
+import com.android.cts.util.ResultUnit;
+
+/**
+ * Test Case class that handles gracefully sensors that are not available in the device.
+ */
+public abstract class SensorTestCase extends CtsAndroidTestCase {
+    protected final String LOG_TAG = "TestRunner";
+
+    protected SensorTestCase() {}
+
+    @Override
+    public void runTest() throws Throwable {
+        try {
+            super.runTest();
+        } catch (SensorNotSupportedException e) {
+            // the sensor is not supported/available in the device, log a warning and skip the test
+            Log.w(LOG_TAG, e.getMessage());
+        }
+    }
+
+    /**
+     * Utility method to log selected stats to a {@link ReportLog} object.  The stats must be
+     * a number or an array of numbers.
+     */
+    public static void logSelectedStatsToReportLog(Instrumentation instrumentation, int depth,
+            String[] keys, SensorStats stats) {
+        DeviceReportLog reportLog = new DeviceReportLog(depth);
+
+        for (String key : keys) {
+            Object value = stats.getValue(key);
+            if (value instanceof Integer) {
+                reportLog.printValue(key, (Integer) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof Double) {
+                reportLog.printValue(key, (Double) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof Float) {
+                reportLog.printValue(key, (Float) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof double[]) {
+                reportLog.printArray(key, (double[]) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof float[]) {
+                float[] tmpFloat = (float[]) value;
+                double[] tmpDouble = new double[tmpFloat.length];
+                for (int i = 0; i < tmpDouble.length; i++) tmpDouble[i] = tmpFloat[i];
+                reportLog.printArray(key, tmpDouble, ResultType.NEUTRAL, ResultUnit.NONE);
+            }
+        }
+
+        reportLog.printSummary("summary", 0, ResultType.NEUTRAL, ResultUnit.NONE);
+        reportLog.deliverReportToHost(instrumentation);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
new file mode 100644
index 0000000..cd6adb1
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -0,0 +1,628 @@
+/*
+ * 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;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+
+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.
+ * <p>
+ * To execute these test cases, the following command can be used:
+ * </p><pre>
+ * adb shell am instrument -e class android.hardware.cts.SingleSensorTests \
+ *     -w com.android.cts.hardware/android.test.InstrumentationCtsTestRunner
+ * </pre><p>
+ * For each sensor that reports continuously, it takes a set of samples. The test suite verifies
+ * that the event ordering, frequency, and jitter pass for the collected sensor events. It
+ * additionally tests that the mean, standard deviation, and magnitude are correct for the sensor
+ * event values, where applicable for a device in a static environment.
+ * </p><p>
+ * The event ordering test verifies the ordering of the sampled data reported by the Sensor under
+ * test. This test is used to guarantee that sensor data is reported in the order it occurs, and
+ * that events are always reported in order. It verifies that each event's timestamp is in the
+ * future compared with the previous event. At the end of the validation, the full set of events is
+ * verified to be ordered by timestamp as they are generated. The test can be susceptible to errors
+ * if the sensor sampled data is not timestamped at the hardware level. Or events sampled at high
+ * rates are added to the FIFO without controlling the appropriate ordering of the events.
+ * </p><p>
+ * The frequency test verifies that the sensor under test can sample and report data at the maximum
+ * frequency (sampling rate) it advertises. The frequency between events is calculated by looking at
+ * the delta between the timestamps associated with each event to get the period. The test is
+ * susceptible to errors if the sensor is not capable to sample data at the maximum rate it
+ * supports, or the sensor events are not timestamped at the hardware level.
+ * </p><p>
+ * The jitter test verifies that the event jittering associated with the sampled data reported by
+ * the sensor under test aligns with the requirements imposed in the CDD. This test characterizes
+ * how the sensor behaves while sampling data at a specific rate. It compares the 95th percentile of
+ * the jittering with a certain percentage of the minimum period. The test is susceptible to errors
+ * if the sensor events are not timestamped at the hardware level.
+ * </p><p>
+ * The mean test verifies that the mean of a set of sampled data from a particular sensor falls into
+ * the expectations defined in the CDD. The verification applies to each axis of the sampled data
+ * reported by the sensor under test. This test is used to validate the requirement imposed by the
+ * CDD to Sensors in Android and characterizes how the Sensor behaves while static. The test is
+ * susceptible to errors if the device is moving while the test is running, or if the sensor's
+ * sampled data indeed varies from the expected mean.
+ * </p><p>
+ * The magnitude test verifies that the magnitude of the sensor data is close to the expected
+ * reference value. The units of the reference value are dependent on the type of sensor.
+ * This test is used to verify that the data reported by the sensor is close to the expected
+ * range and scale. The test calculates the Euclidean norm of the vector represented by the sampled
+ * data and compares it against the test expectations. The test is susceptible to errors when the
+ * sensor under test is uncalibrated, or the units between the data and expectations are different.
+ * </p><p>
+ * The standard deviation test verifies that the standard deviation of a set of sampled data from a
+ * particular sensor falls into the expectations defined in the CDD. The verification applies to
+ * each axis of the sampled data reported by the sensor under test. This test is used to validate
+ * the requirement imposed by the CDD to Sensors in Android and characterizes how the Sensor behaves
+ * while static. The test is susceptible to errors if the device is moving while the test is
+ * running, or if the sensor's sampled data indeed falls into a large standard deviation.
+ * </p>
+ */
+public class SingleSensorTests extends SensorTestCase {
+    private static final String TAG = "SingleSensorTests";
+
+    private static final int BATCHING_OFF = 0;
+    private static final int BATCHING_5S = 5000000;
+
+    private static final int RATE_100HZ = 10000;
+    private static final int RATE_50HZ = 20000;
+    private static final int RATE_25HZ = 40000;
+    private static final int RATE_15HZ = 66667;
+    private static final int RATE_10HZ = 100000;
+    private static final int RATE_5HZ = 200000;
+    private static final int RATE_1HZ = 1000000;
+
+    private static final String[] STAT_KEYS = {
+        SensorStats.FREQUENCY_KEY,
+        SensorStats.JITTER_95_PERCENTILE_KEY,
+        SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY,
+        SensorStats.MAGNITUDE_KEY,
+        SensorStats.MEAN_KEY,
+        SensorStats.STANDARD_DEVIATION_KEY,
+    };
+
+    /**
+     * This test verifies that the sensor's properties complies with the required properites set in
+     * the CDD.
+     * <p>
+     * It checks that the sampling rate advertised by the sensor under test matches that which is
+     * required by the CDD.
+     * </p>
+     */
+    public void testSensorProperties() {
+        // sensor type: [getMinDelay()]
+        Map<Integer, Object[]> expectedProperties = new HashMap<Integer, Object[]>(3);
+        expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{10000});
+        expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{10000});
+        expectedProperties.put(Sensor.TYPE_MAGNETIC_FIELD, new Object[]{100000});
+
+        for (Entry<Integer, Object[]> entry : expectedProperties.entrySet()) {
+            Sensor sensor = SensorCtsHelper.getSensor(getContext(), entry.getKey());
+            String sensorName = SensorTestInformation.getSensorName(entry.getKey());
+            if (entry.getValue()[0] != null) {
+                int expected = (Integer) entry.getValue()[0];
+                String msg = String.format(
+                        "%s: min delay %dus expected to be less than or equal to %dus",
+                        sensorName, sensor.getMinDelay(), expected);
+                assertTrue(msg, sensor.getMinDelay() <= expected);
+            }
+        }
+    }
+
+    // TODO: Figure out if a better way to enumerate test cases programmatically exists that works
+    // with CTS framework.
+    public void testAccelerometer_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testAccelerometer_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testMagneticField_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testMagneticField_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testMagneticField_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_5S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_100HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_25HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_15HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_10HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_5HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_1HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGyroscope_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testGyroscope_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testGyroscope_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testPressure_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testPressure_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testPressure_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGravity_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testGravity_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testGravity_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testRotationVector_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+    public void testRotationVector_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testRotationVector_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testMagneticFieldUncalibrated_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testMagneticFieldUncalibrated_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGameRotationVector_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testGameRotationVector_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGyroscopeUncalibrated_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testGyroscopeUncalibrated_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void  testGeomagneticRotationVector_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void  testGeomagneticRotationVector_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testGeomagneticRotationVector_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_5S);
+    }
+
+    private void runSensorTest(int sensorType, int rateUs, int maxBatchReportLatencyUs)
+            throws Throwable {
+        TestSensorOperation op = new TestSensorOperation(this.getContext(), sensorType,
+                rateUs, maxBatchReportLatencyUs, 5, TimeUnit.SECONDS);
+        op.setDefaultVerifications();
+        op.setLogEvents(true);
+        try {
+            op.execute();
+
+            // Only report stats if it passes.
+            logSelectedStatsToReportLog(getInstrumentation(), 2, STAT_KEYS,
+                    op.getStats());
+        } finally {
+            SensorStats.logStats(TAG, op.getStats());
+
+            String sensorName = SensorTestInformation.getSanitizedSensorName(sensorType);
+            String sensorRate;
+            if (rateUs == SensorManager.SENSOR_DELAY_FASTEST) {
+                sensorRate = "fastest";
+            } else {
+                sensorRate = String.format("%.0fhz",
+                        SensorCtsHelper.getFrequency(rateUs, TimeUnit.MICROSECONDS));
+            }
+            String batching = maxBatchReportLatencyUs > 0 ? "_batching" : "";
+            String fileName = String.format("single_sensor_%s_%s%s.txt",
+                    sensorName, sensorRate, batching);
+            SensorStats.logStatsToFile(fileName, 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..6075add
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/FrameworkUnitTests.java
@@ -0,0 +1,61 @@
+/*
+ * 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.EventGapVerificationTest;
+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(EventGapVerificationTest.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 c8733ba..ed55b01 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -16,65 +16,51 @@
 package android.hardware.cts.helpers;
 
 import android.content.Context;
-
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 
-import android.os.Environment;
-
-import android.util.Log;
-
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-import java.text.SimpleDateFormat;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
-
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
  * Set of static helper methods for CTS tests.
  */
+//TODO: Refactor this class and SensorTestInformation into several more well defined helper classes
 public class SensorCtsHelper {
+
+    private static long NANOS_PER_MILLI = 1000000;
+
     /**
-     * This is an static class.
+     * Private constructor for static class.
      */
     private SensorCtsHelper() {}
 
-    public static <TValue extends Comparable> TValue get95PercentileValue(
+    /**
+     * Get the value of the 95th percentile using nearest rank algorithm.
+     *
+     * @throws IllegalArgumentException if the collection is null or empty
+     */
+    public static <TValue extends Comparable<? super TValue>> TValue get95PercentileValue(
             Collection<TValue> collection) {
         validateCollection(collection);
 
-        ArrayList<TValue> arrayCopy = new ArrayList<TValue>(collection);
+        List<TValue> arrayCopy = new ArrayList<TValue>(collection);
         Collections.sort(arrayCopy);
 
         // zero-based array index
-        int arrayIndex = (int)(arrayCopy.size() * 0.95) - 1;
-        if(arrayIndex < 0) {
-            arrayIndex = 0;
-        }
+        int arrayIndex = (int) Math.round(arrayCopy.size() * 0.95 + .5) - 1;
 
         return arrayCopy.get(arrayIndex);
     }
 
     /**
-     * Calculates the mean for each of the values in the set of TestSensorEvents.
+     * Calculate the mean of a collection.
+     *
+     * @throws IllegalArgumentException if the collection is null or empty
      */
-    public static void getMeans(TestSensorEvent events[], double means[]) {
-        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;
-        }
-    }
-
     public static <TValue extends Number> double getMean(Collection<TValue> collection) {
         validateCollection(collection);
 
@@ -85,124 +71,43 @@
         return sum / collection.size();
     }
 
+    /**
+     * Calculate the bias-corrected sample variance of a collection.
+     *
+     * @throws IllegalArgumentException if the collection is null or empty
+     */
     public static <TValue extends Number> double getVariance(Collection<TValue> collection) {
         validateCollection(collection);
 
         double mean = getMean(collection);
-        ArrayList<Double> squaredDifferences = new ArrayList<Double>();
+        ArrayList<Double> squaredDiffs = new ArrayList<Double>();
         for(TValue value : collection) {
             double difference = mean - value.doubleValue();
-            squaredDifferences.add(Math.pow(difference, 2));
+            squaredDiffs.add(Math.pow(difference, 2));
         }
 
-        double variance = getMean(squaredDifferences);
-        return variance;
-    }
-
-    public static <TValue extends Number> double getStandardDeviation(Collection<TValue> collection) {
-        validateCollection(collection);
-
-        double variance = getVariance(collection);
-        return Math.sqrt(variance);
+        double sum = 0.0;
+        for (Double value : squaredDiffs) {
+            sum += value;
+        }
+        return sum / (squaredDiffs.size() - 1);
     }
 
     /**
-     * Gets the jitter values associated with a set of sensor events.
+     * Calculate the bias-corrected standard deviation of a collection.
      *
-     * @param events The events to use to obtain the jittering information.
-     * @param jitterValues The Collection that will contain the computed jitter values.
-     * @return The mean of the jitter Values.
+     * @throws IllegalArgumentException if the collection is null or empty
      */
-    public static double getJitterMean(TestSensorEvent events[], Collection<Double> jitterValues) {
-        ArrayList<Long> timestampDelayValues = new ArrayList<Long>();
-        double averageTimestampDelay = SensorCtsHelper.getAverageTimestampDelayWithValues(events,
-                timestampDelayValues);
-        for(long frequency : timestampDelayValues) {
-            jitterValues.add(Math.abs(averageTimestampDelay - frequency));
-        }
-
-        double jitterMean = SensorCtsHelper.getMean(timestampDelayValues);
-        return jitterMean;
+    public static <TValue extends Number> double getStandardDeviation(
+            Collection<TValue> collection) {
+        return Math.sqrt(getVariance(collection));
     }
 
     /**
-     * Gets the frequency values associated with a set of sensor events.
-     *
-     * @param events The events to use to obtain the frequency information.
-     * @param timestampDelayValues The Collection that will contain the computed frequency values.
-     * @return The mean of the frequency values.
+     * Get the default sensor for a given type.
      */
-    public static double getAverageTimestampDelayWithValues(
-            TestSensorEvent events[],
-            Collection<Long> timestampDelayValues) {
-        for(int i = 1; i < events.length; ++i) {
-            long previousTimestamp = events[i-1].timestamp;
-            long timestamp = events[i].timestamp;
-            timestampDelayValues.add(timestamp - previousTimestamp);
-        }
-
-        double timestampDelayMean = SensorCtsHelper.getMean(timestampDelayValues);
-        return timestampDelayMean;
-    }
-
-    public static int getSecondsAsMicroSeconds(int seconds) {
-        return (int) TimeUnit.MICROSECONDS.convert(seconds, TimeUnit.SECONDS);
-    }
-
-    /**
-     * NOTE:
-     * - The bug report is usually written to /sdcard/Downloads
-     * - In order for the test Instrumentation to gather useful data the following permissions are
-     *   required:
-     *      . android.permission.READ_LOGS
-     *      . android.permission.DUMP
-     */
-    public static String collectBugreport(String collectorId)
-            throws IOException, InterruptedException {
-        String commands[] = new String[] {
-                "dumpstate",
-                "dumpsys",
-                "logcat -d -v threadtime",
-                "exit"
-        };
-
-        SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-y_H:m:s.S");
-        String outputFile = String.format(
-                "%s/%s_%s",
-                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
-                collectorId,
-                dateFormat.format(new Date()));
-
-        DataOutputStream processOutput = null;
-        try {
-            Process process = Runtime.getRuntime().exec("/system/bin/sh -");
-            processOutput = new DataOutputStream(process.getOutputStream());
-
-            for(String command : commands) {
-                processOutput.writeBytes(String.format("%s >> %s\n", command, outputFile));
-            }
-
-            processOutput.flush();
-            process.waitFor();
-
-            Log.d(collectorId, String.format("Bug-Report collected at: %s", outputFile));
-        } finally {
-            if(processOutput != null) {
-                try {
-                    processOutput.close();
-                } catch(IOException e) {}
-            }
-        }
-
-        return outputFile;
-    }
-
     public static Sensor getSensor(Context context, int sensorType) {
-        SensorManager sensorManager = (SensorManager)context.getSystemService(
-                Context.SENSOR_SERVICE);
-        if(sensorManager == null) {
-            throw new IllegalStateException("SensorService is not present in the system.");
-        }
+        SensorManager sensorManager = getSensorManager(context);
         Sensor sensor = sensorManager.getDefaultSensor(sensorType);
         if(sensor == null) {
             throw new SensorNotSupportedException(sensorType);
@@ -210,52 +115,137 @@
         return sensor;
     }
 
-    public static <TReference extends Number> double getFrequencyInHz(TReference samplingRateInUs) {
-        return 1000000000 / samplingRateInUs.doubleValue();
-    }
-
-    public static String formatAssertionMessage(
-            String verificationName,
-            Sensor sensor,
-            String format,
-            Object ... params) {
-        return formatAssertionMessage(verificationName, null, sensor, format, params);
-    }
-
-    public static String formatAssertionMessage(
-            String verificationName,
-            SensorTestOperation test,
-            Sensor sensor,
-            String format,
-            Object ... params) {
-        StringBuilder builder = new StringBuilder();
-
-        // identify the verification
-        builder.append(verificationName);
-        builder.append("| ");
-        // add test context information
-        if(test != null) {
-            builder.append(test.toString());
-            builder.append("| ");
+    /**
+     * Get all the sensors for a given type.
+     */
+    public static List<Sensor> getSensors(Context context, int sensorType) {
+        SensorManager sensorManager = getSensorManager(context);
+        List<Sensor> sensors = sensorManager.getSensorList(sensorType);
+        if (sensors.size() == 0) {
+            throw new SensorNotSupportedException(sensorType);
         }
-        // add context information
-        builder.append(
-                SensorTestInformation.getSensorName(sensor.getType()));
-        builder.append(", handle:");
-        builder.append(sensor.getHandle());
-        builder.append("| ");
-        // add the custom formatting
-        builder.append(String.format(format, params));
-
-        return builder.toString();
+        return sensors;
     }
 
     /**
-     * Private helpers
+     * Convert a period to frequency in Hz.
      */
-    private static void validateCollection(Collection collection) {
+    public static <TValue extends Number> double getFrequency(TValue period, TimeUnit unit) {
+        return 1000000000 / (TimeUnit.NANOSECONDS.convert(1, unit) * period.doubleValue());
+    }
+
+    /**
+     * Convert a frequency in Hz into a period.
+     */
+    public static <TValue extends Number> double getPeriod(TValue frequency, TimeUnit unit) {
+        return 1000000000 / (TimeUnit.NANOSECONDS.convert(1, unit) * frequency.doubleValue());
+    }
+
+    /**
+     * Convert number of seconds to number of microseconds.
+     */
+    public static int getSecondsAsMicroSeconds(int seconds) {
+        return (int) TimeUnit.MICROSECONDS.convert(seconds, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Convert the sensor delay or rate in microseconds into delay in microseconds.
+     * <p>
+     * The flags SensorManager.SENSOR_DELAY_[GAME|UI|NORMAL] are not supported since the CDD does
+     * not specify values for these flags. The rate is set to the max of
+     * {@link Sensor#getMinDelay()} and the rate given.
+     * </p>
+     */
+    public static int getDelay(Sensor sensor, int rateUs) {
+        if (!isDelayRateTestable(rateUs)) {
+            throw new IllegalArgumentException("rateUs cannot be SENSOR_DELAY_[GAME|UI|NORMAL]");
+        }
+        int delay;
+        if (rateUs == SensorManager.SENSOR_DELAY_FASTEST) {
+            delay = 0;
+        } else {
+            delay = rateUs;
+        }
+        return Math.max(delay, sensor.getMinDelay());
+    }
+
+    /**
+     * 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 static 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) {
+        long durationNs = TimeUnit.NANOSECONDS.convert(duration, timeUnit);
+        try {
+            Thread.sleep(durationNs / NANOS_PER_MILLI, (int) (durationNs % NANOS_PER_MILLI));
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Format an assertion message.
+     *
+     * @param sensor the {@link Sensor}
+     * @param label the verification name
+     * @param rateUs the rate of the sensor
+     * @param maxBatchReportLatencyUs the max batch report latency of the sensor
+     * @return The formatted string
+     */
+    public static String formatAssertionMessage(Sensor sensor, String label, int rateUs,
+            int maxBatchReportLatencyUs) {
+        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 rateUs the rate of the sensor
+     * @param maxBatchReportLatencyUs the max batch report latency of the sensor
+     * @param format the additional format string
+     * @param params the additional format params
+     * @return The formatted string
+     */
+    public static String formatAssertionMessage(Sensor sensor, String label, int rateUs,
+            int maxBatchReportLatencyUs, String format, Object ... params) {
+        return String.format("%s | %s, handle: %d, rateUs: %d, maxBatchReportLatencyUs: %d | %s",
+                label, SensorTestInformation.getSensorName(sensor.getType()), sensor.getHandle(),
+                rateUs, maxBatchReportLatencyUs, String.format(format, params));
+    }
+
+    /**
+     * Validate that a collection is not null or empty.
+     *
+     * @throws IllegalStateException if collection is null or empty.
+     */
+    private static <T> void validateCollection(Collection<T> collection) {
         if(collection == null || collection.size() == 0) {
             throw new IllegalStateException("Collection cannot be null or empty");
         }
     }
+
+    /**
+     * Get the SensorManager.
+     *
+     * @throws IllegalStateException if the SensorManager is not present in the system.
+     */
+    private static SensorManager getSensorManager(Context context) {
+        SensorManager sensorManager = (SensorManager) context.getSystemService(
+                Context.SENSOR_SERVICE);
+        if(sensorManager == null) {
+            throw new IllegalStateException("SensorService is not present in the system.");
+        }
+        return sensorManager;
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
new file mode 100644
index 0000000..6f99692
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for the {@link SensorCtsHelper} class.
+ */
+public class SensorCtsHelperTest extends TestCase {
+
+    /**
+     * Test {@link SensorCtsHelper#get95PercentileValue(Collection)}.
+     */
+    public void testGet95PercentileValue() {
+        Collection<Integer> values = new HashSet<Integer>();
+        for (int i = 0; i < 100; i++) {
+            values.add(i);
+        }
+        assertEquals(95, (int) SensorCtsHelper.get95PercentileValue(values));
+
+        values = new HashSet<Integer>();
+        for (int i = 0; i < 1000; i++) {
+            values.add(i);
+        }
+        assertEquals(950, (int) SensorCtsHelper.get95PercentileValue(values));
+
+        values = new HashSet<Integer>();
+        for (int i = 0; i < 100; i++) {
+            values.add(i * i);
+        }
+        assertEquals(95 * 95, (int) SensorCtsHelper.get95PercentileValue(values));
+    }
+
+    /**
+     * Test {@link SensorCtsHelper#getMean(Collection)}.
+     */
+    public void testGetMean() {
+        List<Integer> values = Arrays.asList(0, 1, 2, 3, 4);
+        double mean = SensorCtsHelper.getMean(values);
+        assertEquals(2.0, mean, 0.00001);
+
+        values = Arrays.asList(1, 2, 3, 4, 5);
+        mean = SensorCtsHelper.getMean(values);
+        assertEquals(3.0, mean, 0.00001);
+
+        values = Arrays.asList(0, 1, 4, 9, 16);
+        mean = SensorCtsHelper.getMean(values);
+        assertEquals(6.0, mean, 0.00001);
+    }
+
+    /**
+     * Test {@link SensorCtsHelper#getVariance(Collection)}.
+     */
+    public void testGetVariance() {
+        List<Integer> values = Arrays.asList(0, 1, 2, 3, 4);
+        double variance = SensorCtsHelper.getVariance(values);
+        assertEquals(2.5, variance, 0.00001);
+
+        values = Arrays.asList(1, 2, 3, 4, 5);
+        variance = SensorCtsHelper.getVariance(values);
+        assertEquals(2.5, variance, 0.00001);
+
+        values = Arrays.asList(0, 2, 4, 6, 8);
+        variance = SensorCtsHelper.getVariance(values);
+        assertEquals(10.0, variance, 0.00001);
+    }
+
+    /**
+     * Test {@link SensorCtsHelper#getStandardDeviation(Collection)}.
+     */
+    public void testGetStandardDeviation() {
+        List<Integer> values = Arrays.asList(0, 1, 2, 3, 4);
+        double stddev = SensorCtsHelper.getStandardDeviation(values);
+        assertEquals(Math.sqrt(2.5), stddev, 0.00001);
+
+        values = Arrays.asList(1, 2, 3, 4, 5);
+        stddev = SensorCtsHelper.getStandardDeviation(values);
+        assertEquals(Math.sqrt(2.5), stddev, 0.00001);
+
+        values = Arrays.asList(0, 2, 4, 6, 8);
+        stddev = SensorCtsHelper.getStandardDeviation(values);
+        assertEquals(Math.sqrt(10.0), stddev, 0.00001);
+    }
+
+    /**
+     * Test {@link SensorCtsHelper#getFrequency(Number, TimeUnit)}.
+     */
+    public void testGetFrequency() {
+        assertEquals(1.0, SensorCtsHelper.getFrequency(1, TimeUnit.SECONDS), 0.001);
+        assertEquals(10.0, SensorCtsHelper.getFrequency(0.1, TimeUnit.SECONDS), 0.001);
+        assertEquals(10.0, SensorCtsHelper.getFrequency(100, TimeUnit.MILLISECONDS), 0.001);
+        assertEquals(1000.0, SensorCtsHelper.getFrequency(1, TimeUnit.MILLISECONDS), 0.001);
+        assertEquals(10000.0, SensorCtsHelper.getFrequency(0.1, TimeUnit.MILLISECONDS), 0.001);
+        assertEquals(10000.0, SensorCtsHelper.getFrequency(100, TimeUnit.MICROSECONDS), 0.001);
+        assertEquals(1000000.0, SensorCtsHelper.getFrequency(1, TimeUnit.MICROSECONDS), 0.001);
+        assertEquals(10000000.0, SensorCtsHelper.getFrequency(0.1, TimeUnit.MICROSECONDS), 0.001);
+        assertEquals(10000000.0, SensorCtsHelper.getFrequency(100, TimeUnit.NANOSECONDS), 0.001);
+        assertEquals(1000000000.0, SensorCtsHelper.getFrequency(1, TimeUnit.NANOSECONDS), 0.001);
+    }
+
+    /**
+     * Test {@link SensorCtsHelper#getPeriod(Number, TimeUnit)}.
+     */
+    public void testGetPeriod() {
+        assertEquals(1.0, SensorCtsHelper.getPeriod(1, TimeUnit.SECONDS), 0.001);
+        assertEquals(0.1, SensorCtsHelper.getPeriod(10, TimeUnit.SECONDS), 0.001);
+        assertEquals(100, SensorCtsHelper.getPeriod(10, TimeUnit.MILLISECONDS), 0.001);
+        assertEquals(1, SensorCtsHelper.getPeriod(1000, TimeUnit.MILLISECONDS), 0.001);
+        assertEquals(0.1, SensorCtsHelper.getPeriod(10000, TimeUnit.MILLISECONDS), 0.001);
+        assertEquals(100, SensorCtsHelper.getPeriod(10000, TimeUnit.MICROSECONDS), 0.001);
+        assertEquals(1, SensorCtsHelper.getPeriod(1000000, TimeUnit.MICROSECONDS), 0.001);
+        assertEquals(0.1, SensorCtsHelper.getPeriod(10000000, TimeUnit.MICROSECONDS), 0.001);
+        assertEquals(100, SensorCtsHelper.getPeriod(10000000, TimeUnit.NANOSECONDS), 0.001);
+        assertEquals(1, SensorCtsHelper.getPeriod(1000000000, TimeUnit.NANOSECONDS), 0.001);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java
deleted file mode 100644
index 6e8b758..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2013 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.content.Context;
-
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
-import android.hardware.SensorManager;
-
-import java.io.Closeable;
-
-import java.security.InvalidParameterException;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import junit.framework.Assert;
-
-/**
- * Test class to wrap SensorManager with verifications and test checks.
- * This class allows to perform operations in the Sensor Manager and performs all the expected test
- * verification on behalf of th owner.
- * An object can be used to quickly writing tests that focus on the scenario that needs to be
- * verified, and not in the implicit verifications that need to take place at any step.
- */
-public class SensorManagerTestVerifier implements Closeable, SensorEventListener2 {
-    private final int WAIT_TIMEOUT_IN_SECONDS = 30;
-
-    private final SensorManager mSensorManager;
-    private final Sensor mSensorUnderTest;
-    private final int mSamplingRateInUs;
-    private final int mReportLatencyInUs;
-
-    private TestSensorListener mEventListener;
-
-    /**
-     * Construction methods.
-     */
-    public SensorManagerTestVerifier(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs) {
-        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
-        mSensorUnderTest = SensorCtsHelper.getSensor(context, sensorType);
-        mSamplingRateInUs = samplingRateInUs;
-        mReportLatencyInUs = reportLatencyInUs;
-
-        mEventListener = new TestSensorListener(mSensorUnderTest, this);
-    }
-
-    /**
-     * Public listeners for Sensor events, these are available for subclasses to implement if they
-     * need access to the raw eventing model.
-     */
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
-
-    @Override
-    public void onSensorChanged(SensorEvent event) {}
-
-    @Override
-    public void onFlushCompleted(Sensor sensor) {}
-
-    /**
-     * Members
-     */
-    public void close() {
-        this.unregisterListener();
-        mEventListener = null;
-    }
-
-    public Sensor getUnderlyingSensor() {
-        return mSensorUnderTest;
-    }
-
-    public void registerListener(String debugInfo) {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                mSamplingRateInUs,
-                mReportLatencyInUs);
-        String message = SensorCtsHelper.formatAssertionMessage(
-                "registerListener",
-                mSensorUnderTest,
-                debugInfo);
-        Assert.assertTrue(message, result);
-    }
-
-    public void registerListener() {
-        this.registerListener("");
-    }
-
-    public void unregisterListener() {
-        mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
-    }
-
-    public TestSensorEvent[] getEvents(int count, String debugInfo) {
-        mEventListener.waitForEvents(count, debugInfo);
-        TestSensorEvent[] events = mEventListener.getAllEvents();
-        mEventListener.clearEvents();
-
-        return events;
-    }
-
-    public TestSensorEvent[] getEvents(int count) {
-        return this.getEvents(count, "");
-    }
-
-    public TestSensorEvent[] getQueuedEvents() {
-        return mEventListener.getAllEvents();
-    }
-
-    public TestSensorEvent[] collectEvents(int eventCount, String debugInfo) {
-        this.registerListener(debugInfo);
-        TestSensorEvent[] events = this.getEvents(eventCount, debugInfo);
-        this.unregisterListener();
-
-        return events;
-    }
-
-    public TestSensorEvent[] collectEvents(int eventCount) {
-        return this.collectEvents(eventCount, "");
-    }
-
-    public void startFlush() {
-        String message = SensorCtsHelper.formatAssertionMessage(
-                "Flush",
-                mSensorUnderTest,
-                "" /* format */);
-        Assert.assertTrue(message, mSensorManager.flush(mEventListener));
-    }
-
-    public void waitForFlush() throws InterruptedException {
-        mEventListener.waitForFlushComplete();
-    }
-
-    public void flush() throws InterruptedException {
-        this.startFlush();
-        this.waitForFlush();
-    }
-
-    /**
-     * Definition of support test classes.
-     */
-    private class TestSensorListener implements SensorEventListener2 {
-        private final Sensor mSensorUnderTest;
-        private final SensorEventListener2 mListener;
-
-        private final ConcurrentLinkedDeque<TestSensorEvent> mSensorEventsList =
-                new ConcurrentLinkedDeque<TestSensorEvent>();
-
-        private volatile CountDownLatch mEventLatch;
-        private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
-
-        public TestSensorListener(Sensor sensor, SensorEventListener2 listener) {
-            if(sensor == null) {
-                throw new InvalidParameterException("sensor cannot be null");
-            }
-            if(listener == null) {
-                throw new InvalidParameterException("listener cannot be null");
-            }
-            mSensorUnderTest = sensor;
-            mListener = listener;
-        }
-
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            // copy the event because there is no better way to do this in the platform
-            mSensorEventsList.addLast(new TestSensorEvent(event));
-            if(mEventLatch != null) {
-                mEventLatch.countDown();
-            }
-            mListener.onSensorChanged(event);
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            mListener.onAccuracyChanged(sensor, accuracy);
-        }
-
-        @Override
-        public void onFlushCompleted(Sensor sensor) {
-            CountDownLatch latch = mFlushLatch;
-            mFlushLatch = new CountDownLatch(1);
-            if(latch != null) {
-                latch.countDown();
-            }
-            mListener.onFlushCompleted(sensor);
-        }
-
-        public void waitForFlushComplete() throws InterruptedException {
-            CountDownLatch latch = mFlushLatch;
-            if(latch != null) {
-                String message = SensorCtsHelper.formatAssertionMessage(
-                        "WaitForFlush",
-                        mSensorUnderTest,
-                        "" /* format */);
-                Assert.assertTrue(message, latch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
-            }
-        }
-
-        public void waitForEvents(int eventCount, String timeoutInfo) {
-            mEventLatch = new CountDownLatch(eventCount);
-            this.clearEvents();
-            try {
-                boolean awaitCompleted = mEventLatch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
-                // TODO: can we collect bug reports on error based only if needed? env var?
-
-                String message = SensorCtsHelper.formatAssertionMessage(
-                        "WaitForEvents",
-                        mSensorUnderTest,
-                        "count:%d, available:%d, %s",
-                        eventCount,
-                        mSensorEventsList.size(),
-                        timeoutInfo);
-                Assert.assertTrue(message, awaitCompleted);
-            } catch(InterruptedException e) {
-            } finally {
-                mEventLatch = null;
-            }
-        }
-
-        public TestSensorEvent[] getAllEvents() {
-            return mSensorEventsList.toArray(new TestSensorEvent[0]);
-        }
-
-        public void clearEvents() {
-            mSensorEventsList.clear();
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
new file mode 100644
index 0000000..1a500d4
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -0,0 +1,182 @@
+/*
+ * 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.ISensorOperation;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+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
+ * together so that they form a tree.
+ */
+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 ERROR = "error";
+    public static final String EVENT_COUNT_KEY = "event_count";
+    public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
+    public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
+    public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
+    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>();
+
+    /**
+     * Add a value.
+     *
+     * @param key the key.
+     * @param value the value as an {@link Object}.
+     */
+    public synchronized void addValue(String key, Object value) {
+        if (value == null) {
+            return;
+        }
+        mValues.put(key, value);
+    }
+
+    /**
+     * Add a nested {@link SensorStats}. This is useful for keeping track of stats in a
+     * {@link ISensorOperation} tree.
+     *
+     * @param key the key
+     * @param stats the sub {@link SensorStats} object.
+     */
+    public synchronized void addSensorStats(String key, SensorStats stats) {
+        if (stats == null) {
+            return;
+        }
+        mSensorStats.put(key, stats);
+    }
+
+    /**
+     * 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
+     * will contain the entry {@code ("key1__key2", "value")}.
+     *
+     * @return a {@link Map} containing all stats from the value and sub {@link SensorStats}.
+     */
+    public synchronized Map<String, Object> flatten() {
+        final Map<String, Object> flattenedMap = new HashMap<String, Object>(mValues);
+        for (Entry<String, SensorStats> statsEntry : mSensorStats.entrySet()) {
+            for (Entry<String, Object> valueEntry : statsEntry.getValue().flatten().entrySet()) {
+                String key = statsEntry.getKey() + DELIMITER + valueEntry.getKey();
+                flattenedMap.put(key, valueEntry.getValue());
+            }
+        }
+        return flattenedMap;
+    }
+
+    /**
+     * Utility method to log the stats to the logcat.
+     */
+    public static void logStats(String tag, SensorStats stats) {
+        final Map<String, Object> flattened = stats.flatten();
+        for (String key : getSortedKeys(flattened)) {
+            Object value = flattened.get(key);
+            Log.v(tag, String.format("%s: %s", key, getValueString(value)));
+        }
+    }
+
+    /**
+     * Utility method to log the stats to a file. Will overwrite the file if it already exists.
+     */
+    public static void logStatsToFile(String fileName, SensorStats stats) throws IOException {
+        final BufferedWriter writer = new BufferedWriter(new FileWriter(
+                new File(Environment.getExternalStorageDirectory(), fileName), false));
+        final Map<String, Object> flattened = stats.flatten();
+        try {
+            for (String key : getSortedKeys(flattened)) {
+                Object value = flattened.get(key);
+                writer.write(String.format("%s: %s\n", key, getValueString(value)));
+            }
+        } finally {
+            writer.flush();
+            writer.close();
+        }
+    }
+
+    private static List<String> getSortedKeys(Map<String, Object> flattenedStats) {
+        List<String> keys = new ArrayList<String>(flattenedStats.keySet());
+        Collections.sort(keys);
+        return keys;
+    }
+
+    private static String getValueString(Object value) {
+        if (value == null) {
+            return "";
+        } else if (value instanceof boolean[]) {
+            return Arrays.toString((boolean[]) value);
+        } else if (value instanceof byte[]) {
+            return Arrays.toString((byte[]) value);
+        } else if (value instanceof char[]) {
+            return Arrays.toString((char[]) value);
+        } else if (value instanceof double[]) {
+            return Arrays.toString((double[]) value);
+        } else if (value instanceof float[]) {
+            return Arrays.toString((float[]) value);
+        } else if (value instanceof int[]) {
+            return Arrays.toString((int[]) value);
+        } else if (value instanceof long[]) {
+            return Arrays.toString((long[]) value);
+        } else if (value instanceof short[]) {
+            return Arrays.toString((short[]) value);
+        } else if (value instanceof Object[]) {
+            return Arrays.toString((Object[]) value);
+        } else {
+            return value.toString();
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStatsTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStatsTest.java
new file mode 100644
index 0000000..8ba0f41
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStatsTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.util.Map;
+
+/**
+ * Unit tests for the {@link SensorStats} class.
+ */
+public class SensorStatsTest extends TestCase {
+
+    /**
+     * Test that {@link SensorStats#flatten()} works correctly.
+     */
+    public void testFlatten() {
+        SensorStats stats = new SensorStats();
+        stats.addValue("value0", 0);
+        stats.addValue("value1", 1);
+
+        SensorStats subStats = new SensorStats();
+        subStats.addValue("value2", 2);
+        subStats.addValue("value3", 3);
+
+        SensorStats subSubStats = new SensorStats();
+        subSubStats.addValue("value4", 4);
+        subSubStats.addValue("value5", 5);
+
+        subStats.addSensorStats("stats1", subSubStats);
+        stats.addSensorStats("stats0", subStats);
+
+        // Add empty stats, expect no value in flattened map
+        stats.addSensorStats("stats2", new SensorStats());
+
+        // Add null values, expect no value in flattened map
+        stats.addSensorStats("stats3", null);
+        stats.addValue("value6", null);
+
+        Map<String, Object> flattened = stats.flatten();
+
+        assertEquals(6, flattened.size());
+        assertEquals(0, (int) (Integer) flattened.get("value0"));
+        assertEquals(1, (int) (Integer) flattened.get("value1"));
+        assertEquals(2, (int) (Integer) flattened.get("stats0__value2"));
+        assertEquals(3, (int) (Integer) flattened.get("stats0__value3"));
+        assertEquals(4, (int) (Integer) flattened.get("stats0__stats1__value4"));
+        assertEquals(5, (int) (Integer) flattened.get("stats0__stats1__value5"));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestCase.java
deleted file mode 100644
index 4bd0eed..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestCase.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2013 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.test.AndroidTestCase;
-
-import android.util.Log;
-
-/**
- * Test Case class that handles gracefully sensors that are not available in the device.
- */
-public abstract class SensorTestCase extends AndroidTestCase {
-    protected final String LOG_TAG = "TestRunner";
-
-    protected SensorTestCase() {}
-
-    @Override
-    public void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (SensorNotSupportedException e) {
-            // the sensor is not supported/available in the device, log a warning and skip the test
-            Log.w(LOG_TAG, e.getMessage());
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
index 90e0706..b220b00 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
@@ -16,137 +16,149 @@
 
 package android.hardware.cts.helpers;
 
-import android.content.Context;
-
 import android.hardware.Sensor;
 
-import java.security.InvalidParameterException;
-
 /**
  * A 'property' bag of sensor information used for testing purposes.
  */
+// TODO: Refactor this class and SensorCtsHelper into several more well defined helper classes
 public class SensorTestInformation {
     private SensorTestInformation() {}
 
-    public static int getAxisCount(int sensorType) {
+    public enum SensorReportingMode {
+        CONTINUOUS,
+        ON_CHANGE,
+        ONE_SHOT,
+    }
+
+    @SuppressWarnings("deprecation")
+    public static SensorReportingMode getReportingMode(int sensorType) {
         switch(sensorType) {
             case Sensor.TYPE_ACCELEROMETER:
-                return 3;
             case Sensor.TYPE_MAGNETIC_FIELD:
-                return 3;
-//            case Sensor.TYPE_ORIENTATION:
-//                return "Orientation";
+            case Sensor.TYPE_ORIENTATION:
             case Sensor.TYPE_GYROSCOPE:
-                return 3;
-//            case Sensor.TYPE_LIGHT:
-//                return "Light";
-//            case Sensor.TYPE_PRESSURE:
-//                return "Pressure";
-//            case Sensor.TYPE_TEMPERATURE:
-//                return "Temperature";
-//            case Sensor.TYPE_PROXIMITY:
-//                return "Proximity";
+            case Sensor.TYPE_PRESSURE:
             case Sensor.TYPE_GRAVITY:
-                return 3;
             case Sensor.TYPE_LINEAR_ACCELERATION:
-                return 3;
-//            case Sensor.TYPE_ROTATION_VECTOR:
-//                return "Rotation Vector";
-//            case Sensor.TYPE_RELATIVE_HUMIDITY:
-//                return "Relative Humidity";
-//            case Sensor.TYPE_AMBIENT_TEMPERATURE:
-//                return "Ambient Temperature";
-//            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
-//                return "Magnetic Field Uncalibrated";
-//            case Sensor.TYPE_GAME_ROTATION_VECTOR:
-//                return "Game Rotation Vector";
-//            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
-//                return "Gyroscope Uncalibrated";
-//            case Sensor.TYPE_SIGNIFICANT_MOTION:
-//                return "Significant Motion";
-//            case Sensor.TYPE_STEP_DETECTOR:
-//                return "Step Detector";
-//            case Sensor.TYPE_STEP_COUNTER:
-//                return "Step Counter";
-//            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
-//                return "Geomagnetic Rotation Vector";
+            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 SensorReportingMode.CONTINUOUS;
+            case Sensor.TYPE_LIGHT:
+            case Sensor.TYPE_TEMPERATURE:
+            case Sensor.TYPE_PROXIMITY:
+            case Sensor.TYPE_RELATIVE_HUMIDITY:
+            case Sensor.TYPE_AMBIENT_TEMPERATURE:
+            case Sensor.TYPE_STEP_DETECTOR:
+            case Sensor.TYPE_STEP_COUNTER:
+                return SensorReportingMode.ON_CHANGE;
+            case Sensor.TYPE_SIGNIFICANT_MOTION:
+                return SensorReportingMode.ONE_SHOT;
             default:
-                throw new InvalidParameterException(
-                        String.format("Invalid sensorType:%d. Unable to find axis count.", sensorType));
+                return null;
         }
     }
 
     public static String getSensorName(int sensorType) {
-        String name;
-        switch(sensorType) {
-            case Sensor.TYPE_ACCELEROMETER:
-                name = "Accelerometer";
-                break;
-            case Sensor.TYPE_MAGNETIC_FIELD:
-                name = "Magnetic Field";
-                break;
-            case Sensor.TYPE_ORIENTATION:
-                name = "Orientation";
-                break;
-            case Sensor.TYPE_GYROSCOPE:
-                name = "Gyroscope";
-                break;
-            case Sensor.TYPE_LIGHT:
-                name = "Light";
-                break;
-            case Sensor.TYPE_PRESSURE:
-                name = "Pressure";
-                break;
-            case Sensor.TYPE_TEMPERATURE:
-                name = "Temperature";
-                break;
-            case Sensor.TYPE_PROXIMITY:
-                name = "Proximity";
-                break;
-            case Sensor.TYPE_GRAVITY:
-                name = "Gravity";
-                break;
-            case Sensor.TYPE_LINEAR_ACCELERATION:
-                name = "Linear Acceleration";
-                break;
-            case Sensor.TYPE_ROTATION_VECTOR:
-                name = "Rotation Vector";
-                break;
-            case Sensor.TYPE_RELATIVE_HUMIDITY:
-                name = "Relative Humidity";
-                break;
-            case Sensor.TYPE_AMBIENT_TEMPERATURE:
-                name = "Ambient Temperature";
-                break;
-            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
-                name = "Magnetic Field Uncalibrated";
-                break;
-            case Sensor.TYPE_GAME_ROTATION_VECTOR:
-                name = "Game Rotation Vector";
-                break;
-            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
-                name = "Gyroscope Uncalibrated";
-                break;
-            case Sensor.TYPE_SIGNIFICANT_MOTION:
-                name = "Significant Motion";
-                break;
-            case Sensor.TYPE_STEP_DETECTOR:
-                name = "Step Detector";
-                break;
-            case Sensor.TYPE_STEP_COUNTER:
-                name = "Step Counter";
-                break;
-            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
-                name = "Geomagnetic Rotation Vector";
-                break;
-            default:
-                name = "<Unknown>";
-        }
-        return String.format("%s (%d)", name, sensorType);
+        return String.format("%s (%d)", getSimpleSensorName(sensorType), sensorType);
     }
 
-    public static int getMaxSamplingRateInUs(Context context, int sensorType) {
-        Sensor sensor = SensorCtsHelper.getSensor(context, sensorType);
-        return sensor.getMinDelay();
+    @SuppressWarnings("deprecation")
+    public static String getSimpleSensorName(int sensorType) {
+        switch(sensorType) {
+            case Sensor.TYPE_ACCELEROMETER:
+                return "Accelerometer";
+            case Sensor.TYPE_MAGNETIC_FIELD:
+                return "Magnetic Field";
+            case Sensor.TYPE_ORIENTATION:
+                return "Orientation";
+            case Sensor.TYPE_GYROSCOPE:
+                return "Gyroscope";
+            case Sensor.TYPE_LIGHT:
+                return "Light";
+            case Sensor.TYPE_PRESSURE:
+                return "Pressure";
+            case Sensor.TYPE_TEMPERATURE:
+                return "Temperature";
+            case Sensor.TYPE_PROXIMITY:
+                return "Proximity";
+            case Sensor.TYPE_GRAVITY:
+                return "Gravity";
+            case Sensor.TYPE_LINEAR_ACCELERATION:
+                return "Linear Acceleration";
+            case Sensor.TYPE_ROTATION_VECTOR:
+                return "Rotation Vector";
+            case Sensor.TYPE_RELATIVE_HUMIDITY:
+                return "Relative Humidity";
+            case Sensor.TYPE_AMBIENT_TEMPERATURE:
+                return "Ambient Temperature";
+            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+                return "Magnetic Field Uncalibrated";
+            case Sensor.TYPE_GAME_ROTATION_VECTOR:
+                return "Game Rotation Vector";
+            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
+                return "Gyroscope Uncalibrated";
+            case Sensor.TYPE_SIGNIFICANT_MOTION:
+                return "Significant Motion";
+            case Sensor.TYPE_STEP_DETECTOR:
+                return "Step Detector";
+            case Sensor.TYPE_STEP_COUNTER:
+                return "Step Counter";
+            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+                return "Geomagnetic Rotation Vector";
+            default:
+                return "<Unknown>";
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public static String getSanitizedSensorName(int sensorType) {
+        switch(sensorType) {
+            case Sensor.TYPE_ACCELEROMETER:
+                return "Accelerometer";
+            case Sensor.TYPE_MAGNETIC_FIELD:
+                return "MagneticField";
+            case Sensor.TYPE_ORIENTATION:
+                return "Orientation";
+            case Sensor.TYPE_GYROSCOPE:
+                return "Gyroscope";
+            case Sensor.TYPE_LIGHT:
+                return "Light";
+            case Sensor.TYPE_PRESSURE:
+                return "Pressure";
+            case Sensor.TYPE_TEMPERATURE:
+                return "Temperature";
+            case Sensor.TYPE_PROXIMITY:
+                return "Proximity";
+            case Sensor.TYPE_GRAVITY:
+                return "Gravity";
+            case Sensor.TYPE_LINEAR_ACCELERATION:
+                return "LinearAcceleration";
+            case Sensor.TYPE_ROTATION_VECTOR:
+                return "RotationVector";
+            case Sensor.TYPE_RELATIVE_HUMIDITY:
+                return "RelativeHumidity";
+            case Sensor.TYPE_AMBIENT_TEMPERATURE:
+                return "AmbientTemperature";
+            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+                return "MagneticFieldUncalibrated";
+            case Sensor.TYPE_GAME_ROTATION_VECTOR:
+                return "GameRotationVector";
+            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
+                return "GyroscopeUncalibrated";
+            case Sensor.TYPE_SIGNIFICANT_MOTION:
+                return "SignificantMotion";
+            case Sensor.TYPE_STEP_DETECTOR:
+                return "StepDetector";
+            case Sensor.TYPE_STEP_COUNTER:
+                return "StepCounter";
+            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+                return "GeomagneticRotationVector";
+            default:
+                return String.format("UnknownSensorType%d", sensorType);
+        }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java
deleted file mode 100644
index 902c802..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2013 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 junit.framework.Assert;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Base test class that supports a basic test operation performed in a sensor.
- * The class follows a command patter as a base for its work.
- *
- * Remarks:
- * - The class wraps verifications and test checks that are needed to verify the operation.
- * - The operation runs in a background thread where it performs the bulk of its work.
- */
-public abstract class SensorTestOperation {
-    private final SensorTestExceptionHandler mExceptionHandler = new SensorTestExceptionHandler();
-
-    protected final String LOG_TAG = "TestRunner";
-    protected final long WAIT_TIMEOUT_IN_MILLISECONDS =
-            TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES);
-
-    private Thread mThread;
-
-    protected int mIterationCount;
-
-    /**
-     * Public API definition.
-     */
-    public synchronized void start() throws Throwable {
-        if(mThread != null) {
-            throw new IllegalStateException("The operation has already been started.");
-        }
-
-        mThread = new Thread() {
-            @Override
-            public void run() {
-                try {
-                    doWork();
-                } catch (Throwable e) {
-                    // log the exception so it can be sent back to the appropriate test thread
-                    this.getUncaughtExceptionHandler().uncaughtException(this, e);
-                }
-            }
-        };
-
-        ++mIterationCount;
-        mThread.setUncaughtExceptionHandler(mExceptionHandler);
-        mThread.start();
-    }
-
-    public synchronized void waitForCompletion() throws Throwable {
-        if(mThread == null) {
-            // let a wait on a stopped operation to be no-op
-            return;
-        }
-        mThread.join(WAIT_TIMEOUT_IN_MILLISECONDS);
-        if(mThread.isAlive()) {
-            // the test is hung so collect the state of the system and fail
-            String operationName = this.getClass().getSimpleName();
-            String message = String.format(
-                    "%s hung. %s. BugReport collected at: %s",
-                    operationName,
-                    this.toString(),
-                    SensorCtsHelper.collectBugreport(operationName));
-            Assert.fail(message);
-        }
-        mThread = null;
-        mExceptionHandler.rethrow();
-    }
-
-    public void execute() throws Throwable {
-        this.start();
-        this.waitForCompletion();
-    }
-
-    @Override
-    public String toString() {
-        return String.format("ThreadId:%d, Iteration:%d", mThread.getId(), mIterationCount);
-    }
-
-    /**
-     * Subclasses implement this method to perform the work associated with the operation they
-     * represent.
-     */
-    protected abstract void doWork() throws Throwable;
-
-    /**
-     * Private helpers.
-     */
-    private class SensorTestExceptionHandler implements Thread.UncaughtExceptionHandler {
-        private final Object mLock = new Object();
-
-        private Throwable mThrowable;
-
-        @Override
-        public void uncaughtException(Thread thread, Throwable throwable) {
-            synchronized(mLock) {
-                // the fist exception is in general the one that is more interesting
-                if(mThrowable != null) {
-                    return;
-                }
-                mThrowable = throwable;
-            }
-        }
-
-        public void rethrow() throws Throwable {
-            Throwable throwable;
-            synchronized(mLock) {
-                throwable = mThrowable;
-                mThrowable = null;
-            }
-            if(throwable != null) {
-                throw throwable;
-            }
-        }
-    }
-}
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 1a2cdfe..b349e1b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
@@ -18,24 +18,52 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
 
 /**
- * Test class to wrap SensorEvent.
- * It currently only provides a way to clone SensorEvent data, but in the future it can contain
- * verifications and test checks.
+ * Class for holding information about individual {@link SensorEvent}s.
  */
 public class TestSensorEvent {
     public final Sensor sensor;
     public final long timestamp;
+    public final long receivedTimestamp;
     public final int accuracy;
     public final float values[];
 
-    public TestSensorEvent(SensorEvent event) {
+    /**
+     * Construct a TestSensorEvent from {@link SensorEvent} data and a received timestamp.
+     *
+     * @param event the {@link SensorEvent} to be cloned
+     * @param receivedTimestamp the timestamp when
+     * {@link SensorEventListener2#onSensorChanged(SensorEvent)} was called, in nanoseconds.
+     */
+    public TestSensorEvent(SensorEvent event, long receivedTimestamp) {
         values = new float[event.values.length];
-        System.arraycopy(event.values, 0, values, 0, event.values.length);
+        System.arraycopy(event.values, 0, values, 0, values.length);
 
         sensor = event.sensor;
         timestamp = event.timestamp;
         accuracy = event.accuracy;
+
+        this.receivedTimestamp = receivedTimestamp;
+    }
+
+    /**
+     * Constructor for TestSensorEvent. Exposed for unit testing.
+     */
+    public TestSensorEvent(Sensor sensor, long timestamp, int accuracy, float[] values) {
+        this(sensor, timestamp, timestamp, accuracy, values);
+    }
+
+    /**
+     * Constructor for TestSensorEvent. Exposed for unit testing.
+     */
+    public TestSensorEvent(Sensor sensor, long timestamp, long receivedTimestamp, int accuracy,
+            float[] values) {
+        this.sensor = sensor;
+        this.timestamp = timestamp;
+        this.receivedTimestamp = receivedTimestamp;
+        this.accuracy = accuracy;
+        this.values = values;
     }
 }
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..ddbc8c2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -0,0 +1,184 @@
+/*
+ * 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";
+    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;
+    private boolean mLogEvents = false;
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Set whether or not to log events
+     */
+    public void setLogEvents(boolean log) {
+        mLogEvents = log;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if(mEventLatch != null) {
+            mEventLatch.countDown();
+        }
+        if (mListener != null) {
+            mListener.onSensorChanged(event);
+        }
+        if (mLogEvents) {
+            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: sensor_timestamp=%d, received_timestamp=%d, values=%s",
+                    mSensor.getType(), event.timestamp, System.nanoTime(),
+                    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",
+                        mRateUs, mMaxBatchReportLatencyUs);
+                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",
+                    mRateUs, mMaxBatchReportLatencyUs, "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
new file mode 100644
index 0000000..a45ad70
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -0,0 +1,221 @@
+/*
+ * 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.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorEventListener2;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A test class that performs the actions of {@link SensorManager} on a single sensor. This
+ * class allows for a single sensor to be registered and unregistered as well as performing
+ * operations such as flushing the sensor events and gathering events. This class also manages
+ * performing the test verifications for the sensor manager.
+ * <p>
+ * This class requires that operations are performed in the following order:
+ * <p><ul>
+ * <li>{@link #registerListener(TestSensorEventListener)}</li>
+ * <li>{@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
+ * <li>{@link #unregisterListener()}</li>
+ * </ul><p>Or:</p><ul>
+ * <li>{@link #runSensor(TestSensorEventListener, int)}</li>
+ * </ul><p>Or:</p><ul>
+ * <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 #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 final SensorManager mSensorManager;
+    private final Sensor mSensor;
+    private final int mRateUs;
+    private final int mMaxBatchReportLatencyUs;
+
+    private TestSensorEventListener mTestSensorEventListener = null;
+
+    /**
+     * Construct a {@link TestSensorManager}.
+     */
+    public TestSensorManager(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs) {
+        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        mSensor = SensorCtsHelper.getSensor(context, sensorType);
+        mRateUs = rateUs;
+        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+    }
+
+    /**
+     * Register the listener. This method will perform a no-op if the sensor is already registered.
+     *
+     * @throws AssertionError if there was an error registering the listener with the
+     * {@link SensorManager}
+     */
+    public void registerListener(TestSensorEventListener listener) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return;
+        }
+
+        mTestSensorEventListener = listener != null ? listener : new TestSensorEventListener();
+        mTestSensorEventListener.setSensorInfo(mSensor, mRateUs, mMaxBatchReportLatencyUs);
+
+        String message = SensorCtsHelper.formatAssertionMessage(mSensor, "registerListener",
+                mRateUs, mMaxBatchReportLatencyUs);
+        boolean result = mSensorManager.registerListener(mTestSensorEventListener, mSensor, mRateUs,
+                mMaxBatchReportLatencyUs);
+        Assert.assertTrue(message, result);
+    }
+
+    /**
+     * Unregister the listener. This method will perform a no-op if the sensor is not registered.
+     */
+    public void unregisterListener() {
+        if (mTestSensorEventListener == null) {
+            Log.w(LOG_TAG, "No listener registered, returning.");
+            return;
+        }
+
+        mSensorManager.unregisterListener(mTestSensorEventListener, mSensor);
+        mTestSensorEventListener = null;
+    }
+
+    /**
+     * Wait for a specific number of events.
+     */
+    public void waitForEvents(int eventCount) {
+        if (mTestSensorEventListener == null) {
+            Log.w(LOG_TAG, "No listener registered, returning.");
+            return;
+        }
+
+        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);
+    }
+
+    /**
+     * Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
+     * the sensor is not registered.
+     *
+     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false
+     */
+    public void startFlush() {
+        if (mTestSensorEventListener == null) {
+            return;
+        }
+
+        String message = SensorCtsHelper.formatAssertionMessage(mSensor, "Flush", mRateUs,
+                mMaxBatchReportLatencyUs);
+        Assert.assertTrue(message, mSensorManager.flush(mTestSensorEventListener));
+    }
+
+    /**
+     * Wait for {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will
+     * perform a no-op if the sensor is not registered.
+     *
+     * @throws AssertionError if there is a time out
+     * @throws InterruptedException if the thread was interrupted
+     */
+    public void waitForFlushCompleted() throws InterruptedException {
+        if (mTestSensorEventListener == null) {
+            return;
+        }
+
+        mTestSensorEventListener.waitForFlushComplete();
+    }
+
+    /**
+     * Call {@link SensorManager#flush(SensorEventListener)} and wait for
+     * {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will perform
+     * a no-op if the sensor is not registered.
+     *
+     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false or
+     * if there is a time out
+     * @throws InterruptedException if the thread was interrupted
+     */
+    public void flush() throws InterruptedException {
+        if (mTestSensorEventListener == null) {
+            return;
+        }
+
+        startFlush();
+        waitForFlushCompleted();
+    }
+
+    /**
+     * Register a listener, wait for a specific number of events, and then unregister the listener.
+     */
+    public void runSensor(TestSensorEventListener listener, int eventCount) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return;
+        }
+
+        try {
+            registerListener(listener);
+            waitForEvents(eventCount);
+        } finally {
+            unregisterListener();
+        }
+    }
+
+    /**
+     * Register a listener, wait for a specific duration, and then unregister the listener.
+     */
+    public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return;
+        }
+
+        try {
+            registerListener(listener);
+            waitForEvents(duration, timeUnit);
+        } finally {
+            unregisterListener();
+        }
+    }
+
+    /**
+     * Get the sensor under test.
+     */
+    public Sensor getSensor() {
+        return mSensor;
+    }
+}
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..ae7ea04
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
@@ -0,0 +1,83 @@
+/*
+ * 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 android.os.SystemClock;
+
+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, SystemClock.elapsedRealtimeNanos());
+        for (ISensorVerification verification : mVerifications) {
+            verification.addSensorEvent(testEvent);
+        }
+        super.onSensorChanged(event);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java
deleted file mode 100644
index 3730f4b..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import android.hardware.cts.helpers.SensorTestOperation;
-
-import java.util.ArrayList;
-
-/**
- * A test operation that groups a set of SensorTestOperations and allows to execute them all in
- * parallel.
- * This class can be combined to compose other primitive SensorTestOperations.
- */
-public class ParallelCompositeSensorTestOperation extends SensorTestOperation {
-    private final ArrayList<SensorTestOperation> mOperations = new ArrayList<SensorTestOperation>();
-
-    /**
-     * There is no synchronization
-     * @param operations
-     */
-    public void add(SensorTestOperation ... operations) {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : operations) {
-                mOperations.add(operation);
-            }
-        }
-    }
-
-    @Override
-    protected void doWork() throws Throwable {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : mOperations) {
-                operation.start();
-            }
-            for(SensorTestOperation operation : mOperations) {
-                operation.waitForCompletion();
-            }
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java
deleted file mode 100644
index 7a3c450..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import android.hardware.cts.helpers.SensorTestOperation;
-
-/**
- * High level SensorTestOperation that executes the inner operation in a loop.
- */
-public class RepeatingSensorTestOperation extends SensorTestOperation {
-    private final SensorTestOperation mSensorTestOperation;
-    private final int mRepetitionCount;
-
-    public RepeatingSensorTestOperation(SensorTestOperation operation, int repetitionCount) {
-        mSensorTestOperation = operation;
-        mRepetitionCount = repetitionCount;
-    }
-
-    @Override
-    protected void doWork() throws Throwable {
-        for(int i = 0; i < mRepetitionCount; ++i) {
-            mSensorTestOperation.execute();
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java
deleted file mode 100644
index 4b92168..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import android.hardware.cts.helpers.SensorTestOperation;
-
-import java.util.ArrayList;
-
-/**
- * A test operation that groups a set of SensorTestOperations and allows to execute them in a
- * sequence, each operation executes in the order they are added to the composite container.
- * This class can be combined to compose other primitive SensorTestOperations.
- */
-public class SequentialCompositeSensorTestOperation extends SensorTestOperation {
-    private final ArrayList<SensorTestOperation> mOperations = new ArrayList<SensorTestOperation>();
-
-    /**
-     * There is no synchronization
-     * @param operations
-     */
-    public void add(SensorTestOperation ... operations) {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : operations) {
-                mOperations.add(operation);
-            }
-        }
-    }
-
-    @Override
-    protected void doWork() throws Throwable {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : mOperations) {
-                operation.execute();
-            }
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java
deleted file mode 100644
index bb43b01..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import junit.framework.Assert;
-
-import android.content.Context;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-/**
- * Test Operation class that validates the ordering of sensor events.
- */
-public class VerifyEventOrderingOperation extends SensorTestOperation {
-    private SensorManagerTestVerifier mSensor;
-
-    public VerifyEventOrderingOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs) {
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                reportLatencyInUs);
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent events[] = mSensor.collectEvents(100);
-        for(int i = 1; i < events.length; ++i) {
-            long previousTimestamp = events[i-1].timestamp;
-            long timestamp = events[i].timestamp;
-            // allow two identical timestamps to be considered in order, in case the resolution of
-            // the timestamp is not granular enough
-            if(previousTimestamp > timestamp) {
-                String message = SensorCtsHelper.formatAssertionMessage(
-                        "Ordering",
-                        this,
-                        mSensor.getUnderlyingSensor(),
-                        "position:%d, previous:%d, timestamp:%d",
-                        i,
-                        previousTimestamp,
-                        timestamp);
-                Assert.fail(message);
-            }
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java
deleted file mode 100644
index 6f1c03a..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import junit.framework.Assert;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import android.util.Log;
-
-import java.security.InvalidParameterException;
-
-import java.util.ArrayList;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test Operation class that validates the sampling rate jittering of a given sensor.
- *
- * Remarks:
- * - In order to guarantee proper results in any environment, the maximum sampling rate supported by
- *   the Sensor is used, this guarantees the frequency reference for the test.
- */
-public class VerifyJitteringOperation extends SensorTestOperation {
-    protected SensorManagerTestVerifier mSensor;
-    protected long mExpectedtimestampInNs;
-    protected long mThresholdPercentage;
-    protected long mThresholdInNs;
-
-    public VerifyJitteringOperation(
-            Context context,
-            int sensorType,
-            int reportLatencyInUs,
-            int thresholdPercentageOfNs) throws InvalidParameterException {
-        if(thresholdPercentageOfNs < 0) {
-            throw new InvalidParameterException("thresholdPercentageOfNs needs to be >= 0");
-        }
-        // use the max sampling frequency the sensor reports to guarantee the results
-        int maxSamplingRateInUs = SensorTestInformation.getMaxSamplingRateInUs(context, sensorType);
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                maxSamplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mExpectedtimestampInNs = TimeUnit.NANOSECONDS.convert(
-                maxSamplingRateInUs,
-                TimeUnit.MICROSECONDS);
-        mThresholdPercentage = thresholdPercentageOfNs;
-        mThresholdInNs = mExpectedtimestampInNs / mThresholdPercentage;
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent events[] = mSensor.collectEvents(100);
-        ArrayList<Double> jitterValues = new ArrayList<Double>();
-        double jitterMean = SensorCtsHelper.getJitterMean(events, jitterValues);
-        double percentile95InNs = SensorCtsHelper.get95PercentileValue(jitterValues);
-
-        if(percentile95InNs > mThresholdInNs) {
-            for(double jitter : jitterValues) {
-                Log.e(LOG_TAG, "Jitter: " + jitter);
-            }
-            double actualPercentValue = (percentile95InNs * 100) / jitterMean;
-            String message = SensorCtsHelper.formatAssertionMessage(
-                    "Jitter(95%%ile)",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    "expected:%dns(%d%%), actual:%fns(%.2f%%)",
-                    mThresholdInNs,
-                    mThresholdPercentage,
-                    percentile95InNs,
-                    actualPercentValue);
-            Assert.fail(message);
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java
deleted file mode 100644
index 3cdffe0..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import junit.framework.Assert;
-
-import android.content.Context;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import android.util.Log;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test Operation class that validates the max sampling rate of a given sensor.
- *
- * Remarks:
- * - In order to guarantee proper results in any environment, the maximum sampling rate supported by
- *   the Sensor is used, this guarantees the frequency reference for the test.
- */
-public class VerifyMaximumFrequencyOperation extends SensorTestOperation {
-    protected SensorManagerTestVerifier mSensor;
-    protected long mExpectedTimestampInNs;
-    protected long mThresholdPercentage;
-    protected long mThresholdInNs;
-
-    public VerifyMaximumFrequencyOperation(
-            Context context,
-            int sensorType,
-            int reportLatencyInUs,
-            int thresholdPercentageOfNs) throws InvalidParameterException {
-        if(thresholdPercentageOfNs < 0) {
-            throw new InvalidParameterException("thresholdPercentageOfNs needs to be >= 0");
-        }
-        // use the max sampling frequency the sensor reports to guarantee the results
-        int maxSamplingRateInUs = SensorTestInformation.getMaxSamplingRateInUs(context, sensorType);
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                maxSamplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mExpectedTimestampInNs = TimeUnit.NANOSECONDS.convert(
-                maxSamplingRateInUs,
-                TimeUnit.MICROSECONDS);
-        mThresholdPercentage = thresholdPercentageOfNs;
-        mThresholdInNs = mExpectedTimestampInNs / mThresholdPercentage;
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent events[] = mSensor.collectEvents(100);
-        ArrayList<Long> timestampDelayValues = new ArrayList<Long>();
-        Double frequencyMeanInUs = SensorCtsHelper.getAverageTimestampDelayWithValues(
-                events,
-                timestampDelayValues);
-
-        if(Math.abs(mExpectedTimestampInNs - frequencyMeanInUs) > mThresholdInNs) {
-            for(long value : timestampDelayValues) {
-                Log.e(LOG_TAG, "TimestampDelay: " + value);
-            }
-            String message = SensorCtsHelper.formatAssertionMessage(
-                    "Frequency",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    "expected:%dns(%.2fHz), actual:%fns(%.2fHz), threshold:%dns(%d%%)",
-                    mExpectedTimestampInNs,
-                    SensorCtsHelper.getFrequencyInHz(mExpectedTimestampInNs),
-                    frequencyMeanInUs,
-                    SensorCtsHelper.getFrequencyInHz(frequencyMeanInUs),
-                    mThresholdInNs,
-                    mThresholdPercentage);
-            Assert.fail(message);
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMeasurementsOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMeasurementsOperation.java
deleted file mode 100644
index d0c991b3..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMeasurementsOperation.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 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.sensorTestOperations;
-
-import junit.framework.Assert;
-
-import android.content.Context;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import java.security.InvalidParameterException;
-
-/**
- * Test Operation class that validates the measurements of a a given sensor.
- * The operation relies on the number of axes each sensor type reports.
- * The verification calculates the mean for each axis on the measurements, and verifies that they
- * fall into the expected intervals.
- */
-public class VerifyMeasurementsOperation extends SensorTestOperation {
-    private final SensorManagerTestVerifier mSensor;
-    private final int mAxisCount;
-    private final double mReferenceValues[];
-    private final double mThreshold;
-
-    public VerifyMeasurementsOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs,
-            double referenceValues[],
-            float threshold) {
-        mAxisCount = SensorTestInformation.getAxisCount(sensorType);
-        if(mAxisCount != referenceValues.length) {
-            throw new InvalidParameterException(
-                    String.format("%d reference values are expected.", mAxisCount));
-        }
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mReferenceValues = referenceValues;
-        mThreshold = threshold;
-    }
-
-    @Override
-    public void doWork() {
-        final String VALUE_SEPARATOR = ", ";
-        TestSensorEvent events[] = mSensor.collectEvents(100);
-        double measuredValues[] = new double[mReferenceValues.length];
-        SensorCtsHelper.getMeans(events, measuredValues);
-
-        boolean success = true;
-        StringBuilder referenceValuesBuilder = new StringBuilder();
-        StringBuilder measuredValuesBuilder = new StringBuilder();
-        for(int i = 0; i < mReferenceValues.length; i++) {
-            double reference = mReferenceValues[i];
-            double measurement = measuredValues[i];
-            double delta = Math.abs(reference - measurement);
-            success &= (delta <= mThreshold);
-            referenceValuesBuilder.append(reference);
-            referenceValuesBuilder.append(VALUE_SEPARATOR);
-            measuredValuesBuilder.append(measurement);
-            measuredValuesBuilder.append(VALUE_SEPARATOR);
-        }
-        if(!success) {
-            String message = SensorCtsHelper.formatAssertionMessage(
-                    "Measurement",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    "expected:( %s), threshold:%f, actual: ( %s)",
-                    referenceValuesBuilder.toString(),
-                    mThreshold,
-                    measuredValuesBuilder.toString());
-            Assert.fail(message);
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyNormOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyNormOperation.java
deleted file mode 100644
index cce3412..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyNormOperation.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import junit.framework.Assert;
-
-import android.content.Context;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-/**
- * Test Operation class that validates the norm of a given sensor.
- * The operation relies in the number of axes each sensor type reports.
- */
-public class VerifyNormOperation extends SensorTestOperation {
-    private SensorManagerTestVerifier mSensor;
-    private int mAxisCount;
-    private double mReferenceValue;
-    private double mThreshold;
-
-    public VerifyNormOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            float referenceValue,
-            float threshold) {
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                0 /*reportLatencyInUs*/);
-        // set expectations
-        mAxisCount = SensorTestInformation.getAxisCount(mSensor.getUnderlyingSensor().getType());
-        mReferenceValue = referenceValue;
-        mThreshold = threshold;
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent event = mSensor.collectEvents(1)[0];
-        StringBuilder valuesBuilder = new StringBuilder();
-        double norm = 0.0;
-
-        for(int i = 0; i < mAxisCount; ++i) {
-            float value = event.values[i];
-            norm += Math.pow(value, 2);
-
-            valuesBuilder.append(value);
-            valuesBuilder.append(", ");
-        }
-        norm = Math.sqrt(norm);
-
-        String message = SensorCtsHelper.formatAssertionMessage(
-                "Norm",
-                this,
-                mSensor.getUnderlyingSensor(),
-                "expected:%f, threshold:%f, actual:%f ( %s)",
-                mReferenceValue,
-                mThreshold,
-                norm,
-                valuesBuilder.toString());
-        Assert.assertTrue(message, Math.abs(mReferenceValue - norm) <= mThreshold);
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifySignumOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifySignumOperation.java
deleted file mode 100644
index 0ccf92a..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifySignumOperation.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 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.sensorTestOperations;
-
-import junit.framework.Assert;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import java.security.InvalidParameterException;
-
-/**
- * Test Operation class that validates the sign of measurements of a a given sensor.
- * The operation relies in the number of axes each sensor type reports.
- */
-public class VerifySignumOperation extends SensorTestOperation {
-    private final SensorManagerTestVerifier mSensor;
-    private final int mAxisCount;
-    private final double mReferenceValues[];
-    private final double mNoiseThreshold;
-
-    /**
-     * @param noiseThreshold Defines the threshold that needs to be crossed to consider a
-     *                       measurement different from zero
-     */
-    public VerifySignumOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            double referenceValues[],
-            double noiseThreshold) {
-        mAxisCount = SensorTestInformation.getAxisCount(sensorType);
-        if(mAxisCount != referenceValues.length) {
-            throw new InvalidParameterException(
-                    String.format("%d reference values are expected.", mAxisCount));
-        }
-        for(int i = 0; i < referenceValues.length; ++i) {
-            double value = referenceValues[i];
-            if(value != 0 && value != -1 && value != +1) {
-                throw new InvalidParameterException(
-                        "A ReferenceValue can only be one of the following: -1, 0, +1");
-            }
-        }
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                0 /*reportLatencyInUs*/);
-        // set expectations
-        mReferenceValues = referenceValues;
-        mNoiseThreshold = noiseThreshold;
-    }
-
-    @Override
-    public void doWork() {
-        final String VALUE_SEPARATOR = ", ";
-        TestSensorEvent events[] = mSensor.collectEvents(100);
-        double measuredValues[] = new double[mReferenceValues.length];
-        SensorCtsHelper.getMeans(events, measuredValues);
-
-        boolean success = true;
-        StringBuilder referenceValuesBuilder = new StringBuilder();
-        StringBuilder measuredValuesBuilder = new StringBuilder();
-        for(int i = 0; i < mReferenceValues.length; i++) {
-            double reference = mReferenceValues[i];
-            double measurement = measuredValues[i];
-            if(reference == 0) {
-                success &= Math.abs(measurement) < mNoiseThreshold;
-            } else {
-                double combinedValue = reference * measurement;
-                if(combinedValue < mNoiseThreshold) {
-                    combinedValue = 0;
-                }
-                success &= combinedValue > 0;
-            }
-            referenceValuesBuilder.append(reference);
-            referenceValuesBuilder.append(VALUE_SEPARATOR);
-            measuredValuesBuilder.append(measurement);
-            measuredValuesBuilder.append(VALUE_SEPARATOR);
-        }
-        if(!success) {
-            String message = SensorCtsHelper.formatAssertionMessage(
-                    "Measurement",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    "expected:( %s), actual:( %s), noiseThreshold:%f",
-                    referenceValuesBuilder.toString(),
-                    measuredValuesBuilder.toString(),
-                    mNoiseThreshold);
-            Assert.fail(message);
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java
deleted file mode 100644
index 89cff94..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2013 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.sensorTestOperations;
-
-import junit.framework.Assert;
-
-import android.content.Context;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Test Operation class that validates the standard deviation of a given sensor.
- */
-public class VerifyStandardDeviationOperation extends SensorTestOperation {
-    private SensorManagerTestVerifier mSensor;
-    private int mAxisCount;
-    private double mExpectedStandardDeviation;
-
-    public VerifyStandardDeviationOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs,
-            float expectedStandardDeviation) {
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mAxisCount = SensorTestInformation.getAxisCount(mSensor.getUnderlyingSensor().getType());
-        mExpectedStandardDeviation = expectedStandardDeviation;
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent events[] = mSensor.collectEvents(100);
-        for(int i = 0; i < mAxisCount; ++i) {
-            ArrayList<Float> values = new ArrayList<Float>();
-            for(TestSensorEvent event : events) {
-                values.add(event.values[i]);
-            }
-
-            double standardDeviation = SensorCtsHelper.getStandardDeviation(values);
-            if(standardDeviation > mExpectedStandardDeviation) {
-                for(float value : values) {
-                    Log.e(LOG_TAG, String.format("SensorValue:%f", value));
-                }
-                String message = SensorCtsHelper.formatAssertionMessage(
-                        "StandardDeviation",
-                        this,
-                        mSensor.getUnderlyingSensor(),
-                        "axis:%d, expected:%f, actual:%f",
-                        i,
-                        mExpectedStandardDeviation,
-                        standardDeviation);
-                Assert.fail(message);
-            }
-        }
-    }
-}
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
new file mode 100644
index 0000000..5b969f2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+/**
+ * A {@link ISensorOperation} which contains a common implementation for gathering
+ * {@link SensorStats}.
+ */
+public abstract class AbstractSensorOperation implements ISensorOperation {
+
+    private final SensorStats mStats = new SensorStats();
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
+     */
+    protected void addSensorStats(String key, SensorStats stats) {
+        mStats.addSensorStats(key, stats);
+    }
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
+     * to be added. This is useful for {@link ISensorOperation}s that have many iterations or child
+     * operations. The key added is in the form {@code key + "_" + index} where index may be zero
+     * padded.
+     */
+    protected void addSensorStats(String key, int index, SensorStats stats) {
+        addSensorStats(String.format("%s_%03d", key, index), stats);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mStats;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public abstract ISensorOperation clone();
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
new file mode 100644
index 0000000..95f1248
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -0,0 +1,140 @@
+/*
+ * 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.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.cts.helpers.SensorStats;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An {@link ISensorOperation} which performs another {@link ISensorOperation} and then wakes up
+ * after a specified period of time and waits for the child operation to complete.
+ * <p>
+ * This operation can be used to allow the device to go to sleep and wake it up after a specified
+ * period of time. After the device wakes up, this operation will hold a wake lock until the child
+ * operation finishes. This operation will not force the device into suspend, so if another
+ * operation is holding a wake lock, the device will stay awake.  Also, if the child operation
+ * finishes before the specified period, this operation return when the child operation finishes
+ * but wake the device one time at the specified period.
+ * </p>
+ */
+public class AlarmOperation extends AbstractSensorOperation {
+    private static final String ACTION = "AlarmOperationAction";
+    private static final String WAKE_LOCK_TAG = "AlarmOperationWakeLock";
+
+    private final ISensorOperation mOperation;
+    private final Context mContext;
+    private final long mSleepDuration;
+    private final TimeUnit mTimeUnit;
+
+    private boolean mCompleted = false;
+    private WakeLock mWakeLock = null;
+
+    /**
+     * Constructor for {@link DelaySensorOperation}
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param context the context used to access the alarm manager
+     * @param sleepDuration the amount of time to sleep
+     * @param timeUnit the unit of the duration
+     */
+    public AlarmOperation(ISensorOperation operation, Context context, long sleepDuration,
+            TimeUnit timeUnit) {
+        mOperation = operation;
+        mContext = context;
+        mSleepDuration = sleepDuration;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        // Start alarm
+        IntentFilter intentFilter = new IntentFilter(ACTION);
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                acquireWakeLock();
+            }
+        };
+        mContext.registerReceiver(receiver, intentFilter);
+
+        AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        long wakeupTimeMs = (System.currentTimeMillis()
+                + TimeUnit.MILLISECONDS.convert(mSleepDuration, mTimeUnit));
+        Intent intent = new Intent(ACTION);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+        am.setExact(AlarmManager.RTC_WAKEUP, wakeupTimeMs, pendingIntent);
+
+        // Execute operation
+        try {
+            mOperation.execute();
+        } finally {
+            releaseWakeLock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mOperation.getStats();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AlarmOperation clone() {
+        return new AlarmOperation(mOperation, mContext, mSleepDuration, mTimeUnit);
+    }
+
+    /**
+     * Method that acquires a wake lock if a wake lock has not already been acquired and if the
+     * operation has not yet completed.
+     */
+    private synchronized void acquireWakeLock() {
+        // Don't acquire wake lock if the operation has already completed.
+        if (mCompleted == true || mWakeLock != null) {
+            return;
+        }
+        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
+    }
+
+    /**
+     * Method that releases the wake lock if it has been acquired.
+     */
+    private synchronized void releaseWakeLock() {
+        mCompleted = true;
+        if (mWakeLock != null) {
+            mWakeLock.release();
+        }
+        mWakeLock = null;
+    }
+}
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
new file mode 100644
index 0000000..bf43189
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -0,0 +1,80 @@
+/*
+ * 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.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An {@link ISensorOperation} which delays for a specified period of time before performing another
+ * {@link ISensorOperation}.
+ */
+public class DelaySensorOperation implements ISensorOperation {
+    private final ISensorOperation mOperation;
+    private final long mDelay;
+    private final TimeUnit mTimeUnit;
+
+    /**
+     * Constructor for {@link DelaySensorOperation}
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param delay the amount of time to delay
+     * @param timeUnit the unit of the delay
+     */
+    public DelaySensorOperation(ISensorOperation operation, long delay, TimeUnit timeUnit) {
+        if (operation == null || timeUnit == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mOperation = operation;
+        mDelay = delay;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        sleep(mDelay, mTimeUnit);
+        mOperation.execute();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mOperation.getStats();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public DelaySensorOperation clone() {
+        return new DelaySensorOperation(mOperation.clone(), mDelay, mTimeUnit);
+    }
+
+    /**
+     * Helper method to sleep for a given number of ns. Exposed for unit testing.
+     */
+    void sleep(long delay, TimeUnit timeUnit) {
+        SensorCtsHelper.sleep(delay, timeUnit);
+    }
+}
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
new file mode 100644
index 0000000..bb64dfa
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A fake {@ISensorOperation} that will run for a specified time and then pass or fail. Useful when
+ * debugging the framework.
+ */
+public class FakeSensorOperation extends AbstractSensorOperation {
+    private static final int NANOS_PER_MILLI = 1000000;
+
+    private final boolean mFail;
+    private final long mDelay;
+    private final TimeUnit mTimeUnit;
+
+    /**
+     * Constructor for {@link FakeSensorOperation} that passes
+     */
+    public FakeSensorOperation(long delay, TimeUnit timeUnit) {
+        this(false, delay, timeUnit);
+    }
+
+    /**
+     * Constructor for {@link FakeSensorOperation}
+     */
+    public FakeSensorOperation(boolean fail, long delay, TimeUnit timeUnit) {
+        if (timeUnit == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mFail = fail;
+        mDelay = delay;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        long delayNs = TimeUnit.NANOSECONDS.convert(mDelay, mTimeUnit);
+        try {
+            Thread.sleep(delayNs / NANOS_PER_MILLI, (int) (delayNs % NANOS_PER_MILLI));
+            getStats().addValue("executed", true);
+            if (mFail) {
+                doFail();
+            }
+        }catch (InterruptedException e) {
+            // Ignore
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FakeSensorOperation clone() {
+        return new FakeSensorOperation(mFail, mDelay, mTimeUnit);
+    }
+
+    /**
+     * Fails the operation.
+     */
+    protected void doFail() {
+        String msg = "FakeSensorOperation failed";
+        getStats().addValue(SensorStats.ERROR, msg);
+        Assert.fail(msg);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
new file mode 100644
index 0000000..4ae56ea
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.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.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+/**
+ * Interface used by all sensor operations. This allows for complex operations such as chaining
+ * operations together or running operations in parallel.
+ * <p>
+ * Certain restrictions exist for {@link ISensorOperation}s:
+ * <p><ul>
+ * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
+ * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
+ * synchronization for operations.</li>
+ * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
+ * the returned value is undefined.</li>
+ * <li>{@link #clone()} may be called any time and should return an operation with the same
+ * parameters as the original.</li>
+ * </ul>
+ */
+public interface ISensorOperation {
+
+    /**
+     * Executes the sensor operation.  This may throw {@link RuntimeException}s such as
+     * {@link AssertionError}s.
+     */
+    public void execute();
+
+    /**
+     * Get the stats for the operation.
+     *
+     * @return The {@link SensorStats} for the operation.
+     */
+    public SensorStats getStats();
+
+    /**
+     * Clones the {@link ISensorOperation}. The implementation should also clone all child
+     * operations, so that a cloned operation will run with the exact same parameters as the
+     * original. The stats should not be cloned.
+     *
+     * @return The cloned {@link ISensorOperation}
+     */
+    public ISensorOperation clone();
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
new file mode 100644
index 0000000..4cca428
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 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;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in parallel.
+ * The children are run in parallel but are given an index label in the order they are added. This
+ * class can be combined to compose complex {@link ISensorOperation}s.
+ */
+public class ParallelSensorOperation extends AbstractSensorOperation {
+    public static final String STATS_TAG = "parallel";
+
+    private static final String TAG = "ParallelSensorOperation";
+    private static final int NANOS_PER_MILLI = 1000000;
+
+    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+    private final Long mTimeout;
+    private final TimeUnit mTimeUnit;
+
+    /**
+     * Constructor for the {@link ParallelSensorOperation} without a timeout.
+     */
+    public ParallelSensorOperation() {
+        mTimeout = null;
+        mTimeUnit = null;
+    }
+
+    /**
+     * Constructor for the {@link ParallelSensorOperation} with a timeout.
+     */
+    public ParallelSensorOperation(long timeout, TimeUnit timeUnit) {
+        if (timeUnit == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mTimeout = timeout;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * Add a set of {@link ISensorOperation}s.
+     */
+    public void add(ISensorOperation ... operations) {
+        for (ISensorOperation operation : operations) {
+            if (operation == null) {
+                throw new IllegalArgumentException("Arguments cannot be null");
+            }
+            mOperations.add(operation);
+        }
+    }
+
+    /**
+     * Executes the {@link ISensorOperation}s in parallel. If an exception occurs one or more
+     * operations, the first exception will be thrown once all operations are completed.
+     */
+    @Override
+    public void execute() {
+        Long timeoutTimeNs = null;
+        if (mTimeout != null && mTimeUnit != null) {
+            timeoutTimeNs = System.nanoTime() + TimeUnit.NANOSECONDS.convert(mTimeout, mTimeUnit);
+        }
+
+        List<OperationThread> threadPool = new ArrayList<OperationThread>(mOperations.size());
+        for (final ISensorOperation operation : mOperations) {
+            OperationThread thread = new OperationThread(operation);
+            thread.start();
+            threadPool.add(thread);
+        }
+
+        List<Integer> timeoutIndices = new ArrayList<Integer>();
+        List<OperationExceptionInfo> exceptions = new ArrayList<OperationExceptionInfo>();
+        Throwable earliestException = null;
+        Long earliestExceptionTime = null;
+
+        for (int i = 0; i < threadPool.size(); i++) {
+            OperationThread thread = threadPool.get(i);
+            join(thread, timeoutTimeNs);
+            if (thread.isAlive()) {
+                timeoutIndices.add(i);
+                thread.interrupt();
+            }
+
+            Throwable exception = thread.getException();
+            Long exceptionTime = thread.getExceptionTime();
+            if (exception != null && exceptionTime != null) {
+                if (exception instanceof AssertionError) {
+                    exceptions.add(new OperationExceptionInfo(i, (AssertionError) exception));
+                }
+                if (earliestExceptionTime == null || exceptionTime < earliestExceptionTime) {
+                    earliestException = exception;
+                    earliestExceptionTime = exceptionTime;
+                }
+            }
+
+            addSensorStats(STATS_TAG, i, thread.getSensorOperation().getStats());
+        }
+
+        if (earliestException == null) {
+            if (timeoutIndices.size() > 0) {
+                Assert.fail(getTimeoutMessage(timeoutIndices));
+            }
+        } else if (earliestException instanceof AssertionError) {
+            String msg = getExceptionMessage(exceptions, timeoutIndices);
+            getStats().addValue(SensorStats.ERROR, msg);
+            throw new AssertionError(msg, earliestException);
+        } else if (earliestException instanceof RuntimeException) {
+            throw (RuntimeException) earliestException;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ParallelSensorOperation clone() {
+        ParallelSensorOperation operation = new ParallelSensorOperation();
+        for (ISensorOperation subOperation : mOperations) {
+            operation.add(subOperation.clone());
+        }
+        return operation;
+    }
+
+    /**
+     * Helper method that joins a thread at a given time in the future.
+     */
+    private void join(Thread thread, Long timeoutTimeNs) {
+        try {
+            if (timeoutTimeNs == null) {
+                thread.join();
+            } else {
+                // Cap wait time to 1ns so that join doesn't block indefinitely.
+                long waitTimeNs = Math.max(timeoutTimeNs - System.nanoTime(), 1);
+                thread.join(waitTimeNs / NANOS_PER_MILLI, (int) waitTimeNs % NANOS_PER_MILLI);
+            }
+        } catch (InterruptedException e) {
+            // Log and ignore
+            Log.w(TAG, "Thread interrupted during join, operations may timeout before expected"
+                    + " time");
+        }
+    }
+
+    /**
+     * Helper method for joining the exception messages used in assertions.
+     */
+    private String getExceptionMessage(List<OperationExceptionInfo> exceptions,
+            List<Integer> timeoutIndices) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(exceptions.get(0).toString());
+        for (int i = 1; i < exceptions.size(); i++) {
+            sb.append(", ").append(exceptions.get(i).toString());
+        }
+        if (timeoutIndices.size() > 0) {
+            sb.append(", ").append(getTimeoutMessage(timeoutIndices));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Helper method for formatting the operation timed out message used in assertions
+     */
+    private String getTimeoutMessage(List<Integer> indices) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Operation");
+        if (indices.size() != 1) {
+            sb.append("s");
+        }
+        sb.append(" ").append(indices.get(0));
+        for (int i = 1; i < indices.size(); i++) {
+            sb.append(", ").append(indices.get(i));
+        }
+        sb.append(" timed out");
+        return sb.toString();
+    }
+
+    /**
+     * Helper class for holding operation index and exception
+     */
+    private class OperationExceptionInfo {
+        private final int mIndex;
+        private final AssertionError mException;
+
+        public OperationExceptionInfo(int index, AssertionError exception) {
+            mIndex = index;
+            mException = exception;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Operation %d failed: \"%s\"", mIndex, mException.getMessage());
+        }
+    }
+
+    /**
+     * Helper class to run the {@link ISensorOperation} in its own thread.
+     */
+    private class OperationThread extends Thread {
+        final private ISensorOperation mOperation;
+        private Throwable mException = null;
+        private Long mExceptionTime = null;
+
+        public OperationThread(ISensorOperation operation) {
+            mOperation = operation;
+        }
+
+        /**
+         * Run the thread catching {@link RuntimeException}s and {@link AssertionError}s and
+         * the time it happened.
+         */
+        @Override
+        public void run() {
+            try {
+                mOperation.execute();
+            } catch (AssertionError e) {
+                mExceptionTime = System.nanoTime();
+                mException = e;
+            } catch (RuntimeException e) {
+                mExceptionTime = System.nanoTime();
+                mException = e;
+            }
+        }
+
+        public ISensorOperation getSensorOperation() {
+            return mOperation;
+        }
+
+        public Throwable getException() {
+            return mException;
+        }
+
+        public Long getExceptionTime() {
+            return mExceptionTime;
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
new file mode 100644
index 0000000..5e023e5
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 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;
+
+/**
+ * A {@link ISensorOperation} that executes a single {@link ISensorOperation} a given number of
+ * times. This class can be combined to compose complex {@link ISensorOperation}s.
+ */
+public class RepeatingSensorOperation extends AbstractSensorOperation {
+    public static final String STATS_TAG = "repeating";
+
+    private final ISensorOperation mOperation;
+    private final int mIterations;
+
+    /**
+     * Constructor for {@link RepeatingSensorOperation}.
+     *
+     * @param operation the {@link ISensorOperation} to run.
+     * @param iterations the number of iterations to run the operation for.
+     */
+    public RepeatingSensorOperation(ISensorOperation operation, int iterations) {
+        if (operation == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mOperation = operation;
+        mIterations = iterations;
+
+    }
+
+    /**
+     * Executes the {@link ISensorOperation}s the given number of times. If an exception occurs
+     * in one iterations, it is thrown and all subsequent iterations will not run.
+     */
+    @Override
+    public void execute() {
+        for(int i = 0; i < mIterations; ++i) {
+            ISensorOperation operation = mOperation.clone();
+            try {
+                operation.execute();
+            } catch (AssertionError e) {
+                String msg = String.format("Iteration %d failed: \"%s\"", i, e.getMessage());
+                getStats().addValue(SensorStats.ERROR, msg);
+                throw new AssertionError(msg, e);
+            } finally {
+                addSensorStats(STATS_TAG, i, operation.getStats());
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public RepeatingSensorOperation clone() {
+        return new RepeatingSensorOperation(mOperation.clone(), mIterations);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
new file mode 100644
index 0000000..7148454
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -0,0 +1,336 @@
+/*
+ * 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;
+
+import junit.framework.TestCase;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for the primitive {@link ISensorOperation}s including {@link DelaySensorOperation},
+ * {@link ParallelSensorOperation}, {@link RepeatingSensorOperation} and
+ * {@link SequentialSensorOperation}.
+ */
+public class SensorOperationTest extends TestCase {
+    private static final int THRESHOLD_MS = 50;
+
+    /**
+     * Test that the {@link FakeSensorOperation} functions correctly. Other tests in this class
+     * rely on this operation.
+     */
+    public void testFakeSensorOperation() {
+        final int opDurationMs = 100;
+
+        ISensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
+
+        assertFalse(op.getStats().flatten().containsKey("executed"));
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(opDurationMs - duration) < THRESHOLD_MS);
+        assertTrue(op.getStats().flatten().containsKey("executed"));
+
+        op = new FakeSensorOperation(true, 0, TimeUnit.MILLISECONDS);
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+        }
+        assertTrue(op.getStats().flatten().keySet().contains(SensorStats.ERROR));
+    }
+
+    /**
+     * Test that the {@link DelaySensorOperation} functions correctly.
+     */
+    public void testDelaySensorOperation() {
+        final int opDurationMs = 500;
+        final int subOpDurationMs = 100;
+
+        FakeSensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+        ISensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(opDurationMs + subOpDurationMs - duration) < THRESHOLD_MS);
+    }
+
+    /**
+     * Test that the {@link ParallelSensorOperation} functions correctly.
+     */
+    public void testParallelSensorOperation() {
+        final int subOpCount = 100;
+        final int subOpDurationMs = 500;
+
+        ParallelSensorOperation op = new ParallelSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+                    TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(subOpDurationMs - duration) < THRESHOLD_MS);
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+    }
+
+    /**
+     * Test that the {@link ParallelSensorOperation} functions correctly if there is a failure in
+     * a child operation.
+     */
+    public void testParallelSensorOperation_fail() {
+        final int subOpCount = 100;
+
+        ParallelSensorOperation op = new ParallelSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            // Trigger failures in the 5th, 55th operations at t=5ms, t=55ms
+            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+            // TODO: Verify that the exception rethrown was at t=5ms.
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount + 3, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+            if (i % 50 == 5) {
+                assertTrue(statsKeys.contains(String.format("%s_%03d%s%s",
+                        ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER,
+                        SensorStats.ERROR)));
+            }
+
+        }
+        assertTrue(statsKeys.contains(SensorStats.ERROR));
+    }
+
+    /**
+     * Test that the {@link ParallelSensorOperation} functions correctly if a child exceeds the
+     * timeout.
+     */
+    public void testParallelSensorOperation_timeout() {
+        final int subOpCount = 100;
+
+        ParallelSensorOperation op = new ParallelSensorOperation(100, TimeUnit.MILLISECONDS);
+        for (int i = 0; i < subOpCount; i++) {
+            // Trigger timeouts in the 5th, 55th operations (5 seconds vs 0 seconds)
+            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+            // TODO: Verify that the exception rethrown was at t=5ms.
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount - 2, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            if (i % 50 != 5) {
+                assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                        ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+            }
+        }
+    }
+
+    /**
+     * Test that the {@link RepeatingSensorOperation} functions correctly.
+     */
+    public void testRepeatingSensorOperation() {
+        final int iterations = 10;
+        final int subOpDurationMs = 100;
+
+        ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(subOpDurationMs * iterations - duration) < THRESHOLD_MS);
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(iterations, statsKeys.size());
+        for (int i = 0; i < iterations; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    RepeatingSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+    }
+
+    /**
+     * Test that the {@link RepeatingSensorOperation} functions correctly if there is a failure in
+     * a child operation.
+     */
+    public void testRepeatingSensorOperation_fail() {
+        final int iterations = 100;
+        final int failCount = 75;
+
+        ISensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
+            private int mExecutedCount = 0;
+            private SensorStats mFakeStats = new SensorStats();
+
+            @Override
+            public void execute() {
+                super.execute();
+                mExecutedCount++;
+
+                if (failCount == mExecutedCount) {
+                    doFail();
+                }
+            }
+
+            @Override
+            public FakeSensorOperation clone() {
+                // Don't clone
+                mFakeStats = new SensorStats();
+                return this;
+            }
+
+            @Override
+            public SensorStats getStats() {
+                return mFakeStats;
+            }
+        };
+        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(failCount + 2, statsKeys.size());
+        for (int i = 0; i < failCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    RepeatingSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+        assertTrue(statsKeys.contains(String.format("%s_%03d%s%s",
+                RepeatingSensorOperation.STATS_TAG, failCount - 1, SensorStats.DELIMITER,
+                SensorStats.ERROR)));
+        assertTrue(statsKeys.contains(SensorStats.ERROR));
+    }
+
+    /**
+     * Test that the {@link SequentialSensorOperation} functions correctly.
+     */
+    public void testSequentialSensorOperation() {
+        final int subOpCount = 10;
+        final int subOpDurationMs = 100;
+
+        SequentialSensorOperation op = new SequentialSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+                    TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(subOpDurationMs * subOpCount - duration) < THRESHOLD_MS);
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    SequentialSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+    }
+
+    /**
+     * Test that the {@link SequentialSensorOperation} functions correctly if there is a failure in
+     * a child operation.
+     */
+    public void testSequentialSensorOperation_fail() {
+        final int subOpCount = 100;
+        final int failCount = 75;
+
+        SequentialSensorOperation op = new SequentialSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            // Trigger a failure in the 75th operation only
+            ISensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
+                    TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(failCount + 2, statsKeys.size());
+        for (int i = 0; i < failCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    SequentialSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+        assertTrue(statsKeys.contains(String.format("%s_%03d%s%s",
+                SequentialSensorOperation.STATS_TAG, failCount - 1, SensorStats.DELIMITER,
+                SensorStats.ERROR)));
+        assertTrue(statsKeys.contains(SensorStats.ERROR));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
new file mode 100644
index 0000000..050a8f6
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in a
+ * sequence. The children are executed in the order they are added. This class can be combined to
+ * compose complex {@link ISensorOperation}s.
+ */
+public class SequentialSensorOperation extends AbstractSensorOperation {
+    public static final String STATS_TAG = "sequential";
+
+    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+
+    /**
+     * Add a set of {@link ISensorOperation}s.
+     */
+    public void add(ISensorOperation ... operations) {
+        for (ISensorOperation operation : operations) {
+            if (operation == null) {
+                throw new IllegalArgumentException("Arguments cannot be null");
+            }
+            mOperations.add(operation);
+        }
+    }
+
+    /**
+     * Executes the {@link ISensorOperation}s in the order they were added. If an exception occurs
+     * in one operation, it is thrown and all subsequent operations will not run.
+     */
+    @Override
+    public void execute() {
+        for (int i = 0; i < mOperations.size(); i++) {
+            ISensorOperation operation = mOperations.get(i);
+            try {
+                operation.execute();
+            } catch (AssertionError e) {
+                String msg = String.format("Operation %d failed: \"%s\"", i, e.getMessage());
+                getStats().addValue(SensorStats.ERROR, msg);
+                throw new AssertionError(msg, e);
+            } finally {
+                addSensorStats(STATS_TAG, i, operation.getStats());
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SequentialSensorOperation clone() {
+        SequentialSensorOperation operation = new SequentialSensorOperation();
+        for (ISensorOperation subOperation : mOperations) {
+            operation.add(subOperation.clone());
+        }
+        return operation;
+    }
+}
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..1be0ba2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -0,0 +1,204 @@
+/*
+ * 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.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.ValidatingSensorEventListener;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+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>();
+
+    private boolean mLogEvents = false;
+
+    /**
+     * 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 whether to log events.
+     */
+    public void setLogEvents(boolean logEvents) {
+        mLogEvents = logEvents;
+    }
+
+    /**
+     * Set all of the default test expectations.
+     */
+    public void setDefaultVerifications() {
+        Sensor sensor = mSensorManager.getSensor();
+        addVerification(EventGapVerification.getDefault(sensor, mRateUs));
+        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() {
+        getStats().addValue("sensor_name", SensorTestInformation.getSensorName(mSensorType));
+        getStats().addValue("sensor_handle", mSensorManager.getSensor().getHandle());
+
+        ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
+        listener.setLogEvents(mLogEvents);
+
+        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) {
+            String msg = SensorCtsHelper.formatAssertionMessage(mSensorManager.getSensor(),
+                    "VerifySensorOperation", mRateUs, mMaxBatchReportLatencyUs, sb.toString());
+            getStats().addValue(SensorStats.ERROR, msg);
+            Assert.fail(msg);
+        }
+    }
+
+    /**
+     * {@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(getStats());
+        } 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/WakeLockOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
new file mode 100644
index 0000000..73da9c9
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -0,0 +1,89 @@
+/*
+ * 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.cts.helpers.SensorStats;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+
+/**
+ * An {@link ISensorOperation} which holds a wakelock while performing another
+ * {@link ISensorOperation}.
+ */
+public class WakeLockOperation extends AbstractSensorOperation {
+    private static final String TAG = "WakeLockOperation";
+
+    private final ISensorOperation mOperation;
+    private final Context mContext;
+    private final int mWakelockFlags;
+
+    /**
+     * Constructor for {@link WakeLockOperation}.
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param context the context used to access the power manager
+     * @param wakelockFlags the flags used when acquiring the wakelock
+     */
+    public WakeLockOperation(ISensorOperation operation, Context context, int wakelockFlags) {
+        mOperation = operation;
+        mContext = context;
+        mWakelockFlags = wakelockFlags;
+    }
+
+    /**
+     * Constructor for {@link WakeLockOperation}.
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param context the context used to access the power manager
+     */
+    public WakeLockOperation(ISensorOperation operation, Context context) {
+        this(operation, context, PowerManager.PARTIAL_WAKE_LOCK);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        WakeLock wakeLock = pm.newWakeLock(mWakelockFlags, TAG);
+
+        wakeLock.acquire();
+        try {
+            mOperation.execute();
+        } finally {
+            wakeLock.release();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mOperation.getStats();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ISensorOperation clone() {
+        return new WakeLockOperation(mOperation, mContext, mWakelockFlags);
+    }
+}
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..911ae3a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -0,0 +1,70 @@
+/*
+ * 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);
+
+    /**
+     * Helper class to store the index, previous event, and current event.
+     */
+    protected class IndexedEventPair {
+        public final int index;
+        public final TestSensorEvent event;
+        public final TestSensorEvent previousEvent;
+
+        public IndexedEventPair(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/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
new file mode 100644
index 0000000..251ef3c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -0,0 +1,117 @@
+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.SensorTestInformation;
+import android.hardware.cts.helpers.SensorTestInformation.SensorReportingMode;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that there are no missing events. This is done by
+ * checking the last received sensor timestamp and checking that it is within 1.8 * the expected
+ * period.
+ */
+public class EventGapVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "missing_event_passed";
+
+    // Fail if no events are delivered within 1.8 times the expected interval
+    private static final double THRESHOLD = 1.8;
+
+    // Number of indices to print in assert message before truncating
+    private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+    private final int mExpectedDelayUs;
+
+    private final List<IndexedEventPair> mEventGaps = new LinkedList<IndexedEventPair>();
+    private TestSensorEvent mPreviousEvent = null;
+    private int mIndex = 0;
+
+    /**
+     * Construct a {@link EventGapVerification}
+     *
+     * @param expectedDelayUs the expected period in us.
+     */
+    public EventGapVerification(int expectedDelayUs) {
+        mExpectedDelayUs = expectedDelayUs;
+    }
+
+    /**
+     * Get the default {@link EventGapVerification}.
+     *
+     * @param sensor the {@link Sensor}
+     * @param rateUs the requested rate in us
+     * @return the verification or null if the verification is not a continuous mode sensor.
+     */
+    public static EventGapVerification getDefault(Sensor sensor, int rateUs) {
+        if (!SensorReportingMode.CONTINUOUS.equals(SensorTestInformation.getReportingMode(
+                sensor.getType()))) {
+            return null;
+        }
+        return new EventGapVerification(SensorCtsHelper.getDelay(sensor, rateUs));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        final int count = mEventGaps.size();
+        stats.addValue(PASSED_KEY, count == 0);
+        stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
+
+        final int[] indices = new int[count];
+        for (int i = 0; i < indices.length; i++) {
+            indices[i] = mEventGaps.get(i).index;
+        }
+        stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, indices);
+
+        if (count > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(count).append(" events gaps: ");
+            for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
+                IndexedEventPair info = mEventGaps.get(i);
+                sb.append(String.format("position=%d, delta_time=%dns; ", info.index,
+                        info.event.timestamp - info.previousEvent.timestamp));
+            }
+            if (count > TRUNCATE_MESSAGE_LENGTH) {
+                sb.append(count - TRUNCATE_MESSAGE_LENGTH).append(" more; ");
+            }
+            sb.append(String.format("(expected <%dns)",
+                    TimeUnit.NANOSECONDS.convert((int) (THRESHOLD * mExpectedDelayUs),
+                            TimeUnit.MICROSECONDS)));
+            Assert.fail(sb.toString());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public EventGapVerification clone() {
+        return new EventGapVerification(mExpectedDelayUs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mPreviousEvent != null) {
+            long deltaNs = event.timestamp - mPreviousEvent.timestamp;
+            long deltaUs = TimeUnit.MICROSECONDS.convert(deltaNs, TimeUnit.NANOSECONDS);
+            if (deltaUs > mExpectedDelayUs * THRESHOLD) {
+                mEventGaps.add(new IndexedEventPair(mIndex, event, mPreviousEvent));
+            }
+        }
+
+        mPreviousEvent = event;
+        mIndex++;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
new file mode 100644
index 0000000..b7861b2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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 EventGapVerification}.
+ */
+public class EventGapVerificationTest extends TestCase {
+
+    /**
+     * Test that the verification passes when there are no results.
+     */
+    public void testVerify_no_events() {
+        // Timestamps in ns, expected in us
+        runVerification(1000, new long[]{}, true, new int[]{});
+    }
+
+    /**
+     * Test that the verification passes when there are not missing events.
+     */
+    public void testVerify_correct() {
+        // Timestamps in ns, expected in us
+        long[] timestamps = {1000000, 2000000, 3000000, 4000000, 5000000};
+        runVerification(1000, timestamps, true, new int[]{});
+    }
+
+    /**
+     * Test that the verification passes when there are not missing events but some jitter.
+     */
+    public void testVerify_jitter() {
+        // Timestamps in ns, expected in us
+        long[] timestamps = {1100000, 2050000, 2990000, 4000000, 4950000};
+        runVerification(1000, timestamps, true, new int[]{});
+    }
+
+    /**
+     * Test that the verification fails when there are missing events.
+     */
+    public void testVerify_missing_events() {
+        // Timestamps in ns, expected in us
+        long[] timestamps = {1000000, 2000000, 3000000, 5000000, 6000000};
+        runVerification(1000, timestamps, false, new int[]{3});
+    }
+
+    private void runVerification(int expected, long[] timestamps, boolean pass,
+            int[] indices) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, timestamps);
+        if (pass) {
+            verification.verify(stats);
+        } else {
+            boolean failed = false;
+            try {
+                verification.verify(stats);
+            } catch (AssertionError e) {
+                // Expected;
+                failed = true;
+            }
+            assertTrue("Expected an AssertionError", failed);
+        }
+        assertEquals(pass, stats.getValue(EventGapVerification.PASSED_KEY));
+        assertEquals(indices.length, stats.getValue(SensorStats.EVENT_GAP_COUNT_KEY));
+        assertNotNull(stats.getValue(SensorStats.EVENT_GAP_POSITIONS_KEY));
+        int[] actualIndices = (int[]) stats.getValue(SensorStats.EVENT_GAP_POSITIONS_KEY);
+        assertEquals(indices.length, actualIndices.length);
+
+        for (int i = 0; i < indices.length; i++) {
+            assertEquals(indices[i], actualIndices[i]);
+        }
+    }
+
+    private ISensorVerification getVerification(int expected, long ... timestamps) {
+        ISensorVerification verification = new EventGapVerification(expected);
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+}
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..c74c826
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.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.Sensor;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.SensorTestInformation.SensorReportingMode;
+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_out_of_order_passed";
+
+    // Number of indices to print in assert message before truncating
+    private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+    private Long mMaxTimestamp = null;
+    private final List<IndexedEventPair> mOutOfOrderEvents = new LinkedList<IndexedEventPair>();
+    private TestSensorEvent mPreviousEvent = null;
+    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) {
+        SensorReportingMode mode = SensorTestInformation.getReportingMode(sensor.getType());
+        if (!SensorReportingMode.CONTINUOUS.equals(mode)
+                && !SensorReportingMode.ON_CHANGE.equals(mode)) {
+            return null;
+        }
+        return new EventOrderingVerification();
+    }
+
+    /**
+     * 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) {
+        final int count = mOutOfOrderEvents.size();
+        stats.addValue(PASSED_KEY, count == 0);
+        stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY, count);
+
+        final int[] indices = new int[count];
+        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 (count > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(count).append(" events out of order: ");
+            for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
+                IndexedEventPair info = mOutOfOrderEvents.get(i);
+                sb.append(String.format("position=%d, previous=%d, timestamp=%d; ", info.index,
+                        info.previousEvent.timestamp, info.event.timestamp));
+            }
+            if (count > TRUNCATE_MESSAGE_LENGTH) {
+                sb.append(count - TRUNCATE_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 IndexedEventPair(mIndex, event, mPreviousEvent));
+            } else if (event.timestamp > mMaxTimestamp) {
+                mMaxTimestamp = event.timestamp;
+            }
+        }
+
+        mPreviousEvent = event;
+        mIndex++;
+    }
+}
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..4815688
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
@@ -0,0 +1,141 @@
+/*
+ * 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.SensorTestInformation;
+import android.hardware.cts.helpers.SensorTestInformation.SensorReportingMode;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+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";
+
+    // lower threshold is (100 - 10)% expected
+    private static final int DEFAULT_LOWER_THRESHOLD = 10;
+    // upper threshold is (100 + 110)% expected
+    private static final int DEFAULT_UPPER_THRESHOLD = 110;
+
+    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 (!SensorReportingMode.CONTINUOUS.equals(
+                SensorTestInformation.getReportingMode(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 * DEFAULT_LOWER_THRESHOLD / 100;
+        double upperThreshold = expected * DEFAULT_UPPER_THRESHOLD / 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++;
+    }
+}
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..6feceb8
--- /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..5e44273
--- /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..d6769d0
--- /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..9428d1d
--- /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..57b34b0
--- /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..308f114
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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 {
+            boolean failed = false;
+            try {
+                verification.verify(stats);
+            } catch (AssertionError e) {
+                // Expected;
+                failed = true;
+            }
+            assertTrue("Expected an AssertionError", failed);
+        }
+        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;
+    }
+}
diff --git a/tests/tests/renderscript/libcoremathtestcpp/CoreMathTestJni.cpp b/tests/tests/renderscript/libcoremathtestcpp/CoreMathTestJni.cpp
index d728fc0..90c8100 100644
--- a/tests/tests/renderscript/libcoremathtestcpp/CoreMathTestJni.cpp
+++ b/tests/tests/renderscript/libcoremathtestcpp/CoreMathTestJni.cpp
@@ -274,7 +274,7 @@
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_maxU8(JNIEnv*, jclass, jbyte x, jbyte y) {
-    return std::max((unsigned char)x, (unsigned char)y);
+    return std::max((uint8_t)x, (uint8_t)y);
 }
 
 extern "C" JNIEXPORT jshort JNICALL
@@ -284,7 +284,7 @@
 
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_maxU16(JNIEnv*, jclass, jshort x, jshort y) {
-    return std::max((unsigned short)x, (unsigned short)y);
+    return std::max((uint16_t)x, (uint16_t)y);
 }
 
 extern "C" JNIEXPORT jint JNICALL
@@ -294,7 +294,7 @@
 
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_maxU32(JNIEnv*, jclass, jint x, jint y) {
-    return std::max((unsigned int)x, (unsigned int)y);
+    return std::max((uint32_t)x, (uint32_t)y);
 }
 
 extern "C" JNIEXPORT jlong JNICALL
@@ -304,7 +304,7 @@
 
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_maxU64(JNIEnv*, jclass, jlong x, jlong y) {
-    return std::max((unsigned long)x, (unsigned long)y);
+    return std::max((uint64_t)x, (uint64_t)y);
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
@@ -314,7 +314,7 @@
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_minU8(JNIEnv*, jclass, jbyte x, jbyte y) {
-    return std::min((unsigned char)x, (unsigned char)y);
+    return std::min((uint8_t)x, (uint8_t)y);
 }
 
 extern "C" JNIEXPORT jshort JNICALL
@@ -324,7 +324,7 @@
 
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_minU16(JNIEnv*, jclass, jshort x, jshort y) {
-    return std::min((unsigned short)x, (unsigned short)y);
+    return std::min((uint16_t)x, (uint16_t)y);
 }
 
 extern "C" JNIEXPORT jint JNICALL
@@ -334,7 +334,7 @@
 
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_minU32(JNIEnv*, jclass, jint x, jint y) {
-    return std::min((unsigned int)x, (unsigned int)y);
+    return std::min((uint32_t)x, (uint32_t)y);
 }
 
 extern "C" JNIEXPORT jlong JNICALL
@@ -344,7 +344,7 @@
 
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_minU64(JNIEnv*, jclass, jlong x, jlong y) {
-    return std::min((unsigned long)x, (unsigned long)y);
+    return std::min((uint64_t)x, (uint64_t)y);
 }
 
 extern "C" JNIEXPORT jfloat JNICALL
@@ -409,363 +409,363 @@
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToChar(JNIEnv*, jclass, jbyte x) {
-    return (jbyte)(signed char)(signed char)x;
+    return (jbyte)(int8_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToUchar(JNIEnv*, jclass, jbyte x) {
-    return (jbyte)(unsigned char)(signed char)x;
+    return (jbyte)(uint8_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToShort(JNIEnv*, jclass, jbyte x) {
-    return (jshort)(short)(signed char)x;
+    return (jshort)(int16_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToUshort(JNIEnv*, jclass, jbyte x) {
-    return (jshort)(unsigned short)(signed char)x;
+    return (jshort)(uint16_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToInt(JNIEnv*, jclass, jbyte x) {
-    return (jint)(int)(signed char)x;
+    return (jint)(int32_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToUint(JNIEnv*, jclass, jbyte x) {
-    return (jint)(unsigned int)(signed char)x;
+    return (jint)(uint32_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToLong(JNIEnv*, jclass, jbyte x) {
-    return (jlong)(long)(signed char)x;
+    return (jlong)(int64_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToUlong(JNIEnv*, jclass, jbyte x) {
-    return (jlong)(unsigned long)(signed char)x;
+    return (jlong)(uint64_t)(int8_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToFloat(JNIEnv*, jclass, jbyte x) {
-    return (jfloat)(float)(signed char)x;
+    return (jfloat)(float)(int8_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertCharToDouble(JNIEnv*, jclass, jbyte x) {
-    return (jdouble)(double)(signed char)x;
+    return (jdouble)(double)(int8_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToChar(JNIEnv*, jclass, jbyte x) {
-    return (jbyte)(signed char)(unsigned char)x;
+    return (jbyte)(int8_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToUchar(JNIEnv*, jclass, jbyte x) {
-    return (jbyte)(unsigned char)(unsigned char)x;
+    return (jbyte)(uint8_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToShort(JNIEnv*, jclass, jbyte x) {
-    return (jshort)(short)(unsigned char)x;
+    return (jshort)(int16_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToUshort(JNIEnv*, jclass, jbyte x) {
-    return (jshort)(unsigned short)(unsigned char)x;
+    return (jshort)(uint16_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToInt(JNIEnv*, jclass, jbyte x) {
-    return (jint)(int)(unsigned char)x;
+    return (jint)(int32_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToUint(JNIEnv*, jclass, jbyte x) {
-    return (jint)(unsigned int)(unsigned char)x;
+    return (jint)(uint32_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToLong(JNIEnv*, jclass, jbyte x) {
-    return (jlong)(long)(unsigned char)x;
+    return (jlong)(int64_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToUlong(JNIEnv*, jclass, jbyte x) {
-    return (jlong)(unsigned long)(unsigned char)x;
+    return (jlong)(uint64_t)(uint8_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToFloat(JNIEnv*, jclass, jbyte x) {
-    return (jfloat)(float)(unsigned char)x;
+    return (jfloat)(float)(uint8_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUcharToDouble(JNIEnv*, jclass, jbyte x) {
-    return (jdouble)(double)(unsigned char)x;
+    return (jdouble)(double)(uint8_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToChar(JNIEnv*, jclass, jshort x) {
-    return (jbyte)(signed char)(short)x;
+    return (jbyte)(int8_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToUchar(JNIEnv*, jclass, jshort x) {
-    return (jbyte)(unsigned char)(short)x;
+    return (jbyte)(uint8_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToShort(JNIEnv*, jclass, jshort x) {
-    return (jshort)(short)(short)x;
+    return (jshort)(int16_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToUshort(JNIEnv*, jclass, jshort x) {
-    return (jshort)(unsigned short)(short)x;
+    return (jshort)(uint16_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToInt(JNIEnv*, jclass, jshort x) {
-    return (jint)(int)(short)x;
+    return (jint)(int32_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToUint(JNIEnv*, jclass, jshort x) {
-    return (jint)(unsigned int)(short)x;
+    return (jint)(uint32_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToLong(JNIEnv*, jclass, jshort x) {
-    return (jlong)(long)(short)x;
+    return (jlong)(int64_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToUlong(JNIEnv*, jclass, jshort x) {
-    return (jlong)(unsigned long)(short)x;
+    return (jlong)(uint64_t)(int16_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToFloat(JNIEnv*, jclass, jshort x) {
-    return (jfloat)(float)(short)x;
+    return (jfloat)(float)(int16_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertShortToDouble(JNIEnv*, jclass, jshort x) {
-    return (jdouble)(double)(short)x;
+    return (jdouble)(double)(int16_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToChar(JNIEnv*, jclass, jshort x) {
-    return (jbyte)(signed char)(unsigned short)x;
+    return (jbyte)(int8_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToUchar(JNIEnv*, jclass, jshort x) {
-    return (jbyte)(unsigned char)(unsigned short)x;
+    return (jbyte)(uint8_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToShort(JNIEnv*, jclass, jshort x) {
-    return (jshort)(short)(unsigned short)x;
+    return (jshort)(int16_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToUshort(JNIEnv*, jclass, jshort x) {
-    return (jshort)(unsigned short)(unsigned short)x;
+    return (jshort)(uint16_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToInt(JNIEnv*, jclass, jshort x) {
-    return (jint)(int)(unsigned short)x;
+    return (jint)(int32_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToUint(JNIEnv*, jclass, jshort x) {
-    return (jint)(unsigned int)(unsigned short)x;
+    return (jint)(uint32_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToLong(JNIEnv*, jclass, jshort x) {
-    return (jlong)(long)(unsigned short)x;
+    return (jlong)(int64_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToUlong(JNIEnv*, jclass, jshort x) {
-    return (jlong)(unsigned long)(unsigned short)x;
+    return (jlong)(uint64_t)(uint16_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToFloat(JNIEnv*, jclass, jshort x) {
-    return (jfloat)(float)(unsigned short)x;
+    return (jfloat)(float)(uint16_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUshortToDouble(JNIEnv*, jclass, jshort x) {
-    return (jdouble)(double)(unsigned short)x;
+    return (jdouble)(double)(uint16_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToChar(JNIEnv*, jclass, jint x) {
-    return (jbyte)(signed char)(int)x;
+    return (jbyte)(int8_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToUchar(JNIEnv*, jclass, jint x) {
-    return (jbyte)(unsigned char)(int)x;
+    return (jbyte)(uint8_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToShort(JNIEnv*, jclass, jint x) {
-    return (jshort)(short)(int)x;
+    return (jshort)(int16_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToUshort(JNIEnv*, jclass, jint x) {
-    return (jshort)(unsigned short)(int)x;
+    return (jshort)(uint16_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToInt(JNIEnv*, jclass, jint x) {
-    return (jint)(int)(int)x;
+    return (jint)(int32_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToUint(JNIEnv*, jclass, jint x) {
-    return (jint)(unsigned int)(int)x;
+    return (jint)(uint32_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToLong(JNIEnv*, jclass, jint x) {
-    return (jlong)(long)(int)x;
+    return (jlong)(int64_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToUlong(JNIEnv*, jclass, jint x) {
-    return (jlong)(unsigned long)(int)x;
+    return (jlong)(uint64_t)(int32_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToFloat(JNIEnv*, jclass, jint x) {
-    return (jfloat)(float)(int)x;
+    return (jfloat)(float)(int32_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertIntToDouble(JNIEnv*, jclass, jint x) {
-    return (jdouble)(double)(int)x;
+    return (jdouble)(double)(int32_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToChar(JNIEnv*, jclass, jint x) {
-    return (jbyte)(signed char)(unsigned int)x;
+    return (jbyte)(int8_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToUchar(JNIEnv*, jclass, jint x) {
-    return (jbyte)(unsigned char)(unsigned int)x;
+    return (jbyte)(uint8_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToShort(JNIEnv*, jclass, jint x) {
-    return (jshort)(short)(unsigned int)x;
+    return (jshort)(int16_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToUshort(JNIEnv*, jclass, jint x) {
-    return (jshort)(unsigned short)(unsigned int)x;
+    return (jshort)(uint16_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToInt(JNIEnv*, jclass, jint x) {
-    return (jint)(int)(unsigned int)x;
+    return (jint)(int32_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToUint(JNIEnv*, jclass, jint x) {
-    return (jint)(unsigned int)(unsigned int)x;
+    return (jint)(uint32_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToLong(JNIEnv*, jclass, jint x) {
-    return (jlong)(long)(unsigned int)x;
+    return (jlong)(int64_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToUlong(JNIEnv*, jclass, jint x) {
-    return (jlong)(unsigned long)(unsigned int)x;
+    return (jlong)(uint64_t)(uint32_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToFloat(JNIEnv*, jclass, jint x) {
-    return (jfloat)(float)(unsigned int)x;
+    return (jfloat)(float)(uint32_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUintToDouble(JNIEnv*, jclass, jint x) {
-    return (jdouble)(double)(unsigned int)x;
+    return (jdouble)(double)(uint32_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToChar(JNIEnv*, jclass, jlong x) {
-    return (jbyte)(signed char)(long)x;
+    return (jbyte)(int8_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToUchar(JNIEnv*, jclass, jlong x) {
-    return (jbyte)(unsigned char)(long)x;
+    return (jbyte)(uint8_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToShort(JNIEnv*, jclass, jlong x) {
-    return (jshort)(short)(long)x;
+    return (jshort)(int16_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToUshort(JNIEnv*, jclass, jlong x) {
-    return (jshort)(unsigned short)(long)x;
+    return (jshort)(uint16_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToInt(JNIEnv*, jclass, jlong x) {
-    return (jint)(int)(long)x;
+    return (jint)(int32_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToUint(JNIEnv*, jclass, jlong x) {
-    return (jint)(unsigned int)(long)x;
+    return (jint)(uint32_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToLong(JNIEnv*, jclass, jlong x) {
-    return (jlong)(long)(long)x;
+    return (jlong)(int64_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToUlong(JNIEnv*, jclass, jlong x) {
-    return (jlong)(unsigned long)(long)x;
+    return (jlong)(uint64_t)(int64_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToFloat(JNIEnv*, jclass, jlong x) {
-    return (jfloat)(float)(long)x;
+    return (jfloat)(float)(int64_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertLongToDouble(JNIEnv*, jclass, jlong x) {
-    return (jdouble)(double)(long)x;
+    return (jdouble)(double)(int64_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToChar(JNIEnv*, jclass, jlong x) {
-    return (jbyte)(signed char)(unsigned long)x;
+    return (jbyte)(int8_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToUchar(JNIEnv*, jclass, jlong x) {
-    return (jbyte)(unsigned char)(unsigned long)x;
+    return (jbyte)(uint8_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToShort(JNIEnv*, jclass, jlong x) {
-    return (jshort)(short)(unsigned long)x;
+    return (jshort)(int16_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToUshort(JNIEnv*, jclass, jlong x) {
-    return (jshort)(unsigned short)(unsigned long)x;
+    return (jshort)(uint16_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToInt(JNIEnv*, jclass, jlong x) {
-    return (jint)(int)(unsigned long)x;
+    return (jint)(int32_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToUint(JNIEnv*, jclass, jlong x) {
-    return (jint)(unsigned int)(unsigned long)x;
+    return (jint)(uint32_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToLong(JNIEnv*, jclass, jlong x) {
-    return (jlong)(long)(unsigned long)x;
+    return (jlong)(int64_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToUlong(JNIEnv*, jclass, jlong x) {
-    return (jlong)(unsigned long)(unsigned long)x;
+    return (jlong)(uint64_t)(uint64_t)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToFloat(JNIEnv*, jclass, jlong x) {
-    return (jfloat)(float)(unsigned long)x;
+    return (jfloat)(float)(uint64_t)x;
 }
 extern "C" JNIEXPORT jdouble JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertUlongToDouble(JNIEnv*, jclass, jlong x) {
-    return (jdouble)(double)(unsigned long)x;
+    return (jdouble)(double)(uint64_t)x;
 }
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToChar(JNIEnv*, jclass, jfloat x) {
-    return (jbyte)(signed char)(float)x;
+    return (jbyte)(int8_t)(float)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToUchar(JNIEnv*, jclass, jfloat x) {
-    return (jbyte)(unsigned char)(float)x;
+    return (jbyte)(uint8_t)(float)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToShort(JNIEnv*, jclass, jfloat x) {
-    return (jshort)(short)(float)x;
+    return (jshort)(int16_t)(float)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToUshort(JNIEnv*, jclass, jfloat x) {
-    return (jshort)(unsigned short)(float)x;
+    return (jshort)(uint16_t)(float)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToInt(JNIEnv*, jclass, jfloat x) {
-    return (jint)(int)(float)x;
+    return (jint)(int32_t)(float)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToUint(JNIEnv*, jclass, jfloat x) {
-    return (jint)(unsigned int)(float)x;
+    return (jint)(uint32_t)(float)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToLong(JNIEnv*, jclass, jfloat x) {
-    return (jlong)(long)(float)x;
+    return (jlong)(int64_t)(float)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToUlong(JNIEnv*, jclass, jfloat x) {
-    return (jlong)(unsigned long)(float)x;
+    return (jlong)(uint64_t)(float)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertFloatToFloat(JNIEnv*, jclass, jfloat x) {
@@ -778,35 +778,35 @@
 
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToChar(JNIEnv*, jclass, jdouble x) {
-    return (jbyte)(signed char)(double)x;
+    return (jbyte)(int8_t)(double)x;
 }
 extern "C" JNIEXPORT jbyte JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToUchar(JNIEnv*, jclass, jdouble x) {
-    return (jbyte)(unsigned char)(double)x;
+    return (jbyte)(uint8_t)(double)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToShort(JNIEnv*, jclass, jdouble x) {
-    return (jshort)(short)(double)x;
+    return (jshort)(int16_t)(double)x;
 }
 extern "C" JNIEXPORT jshort JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToUshort(JNIEnv*, jclass, jdouble x) {
-    return (jshort)(unsigned short)(double)x;
+    return (jshort)(uint16_t)(double)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToInt(JNIEnv*, jclass, jdouble x) {
-    return (jint)(int)(double)x;
+    return (jint)(int32_t)(double)x;
 }
 extern "C" JNIEXPORT jint JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToUint(JNIEnv*, jclass, jdouble x) {
-    return (jint)(unsigned int)(double)x;
+    return (jint)(uint32_t)(double)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToLong(JNIEnv*, jclass, jdouble x) {
-    return (jlong)(long)(double)x;
+    return (jlong)(int64_t)(double)x;
 }
 extern "C" JNIEXPORT jlong JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToUlong(JNIEnv*, jclass, jdouble x) {
-    return (jlong)(unsigned long)(double)x;
+    return (jlong)(uint64_t)(double)x;
 }
 extern "C" JNIEXPORT jfloat JNICALL
 Java_android_renderscript_cts_CoreMathVerifier_convertDoubleToFloat(JNIEnv*, jclass, jdouble x) {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java b/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java
index 46d574f..8841b14 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java
@@ -245,7 +245,7 @@
     // Normalizes the n-dimensional vector, i.e. makes it length 1.
     static private void normalize(float[] in, Floaty[] out, int ulpFactor, int ulpRelaxedFactor) {
         Floaty l = length(in, ulpFactor, ulpRelaxedFactor);
-        boolean isZero = l.getValue() == 0f;
+        boolean isZero = l.getFloatValue() == 0f;
         for (int i = 0; i < in.length; i++) {
             out[i] = new Floaty(in[i], ulpFactor, ulpRelaxedFactor);
             if (!isZero) {
@@ -318,12 +318,12 @@
         args.out = new Floaty(atan(args.inV) / (float) Math.PI, 5, 128);
     }
 
-	static public void computeAtan2(TestAtan2.ArgumentsFloatFloatFloat args) {
-		args.out = new Floaty(atan2(args.inY, args.inX), 6, 128);
-	}
+    static public void computeAtan2(TestAtan2.ArgumentsFloatFloatFloat args) {
+        args.out = new Floaty(atan2(args.inY, args.inX), 6, 128);
+    }
 
     static public void computeAtan2pi(TestAtan2pi.ArgumentsFloatFloatFloat args) {
-		args.out = new Floaty(atan2(args.inY, args.inX) / (float) Math.PI, 6, 128);
+        args.out = new Floaty(atan2(args.inY, args.inX) / (float) Math.PI, 6, 128);
     }
 
     static public void computeCbrt(TestCbrt.ArgumentsFloatFloat args) {
@@ -432,7 +432,7 @@
     }
     /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsCharDouble args) {
-        args.out = convertCharToDouble(args.inV);
+        args.out = new Floaty(convertCharToDouble(args.inV), 0, 0);
     }
     */
 
@@ -467,7 +467,7 @@
     }
     /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsUcharDouble args) {
-        args.out = convertUcharToDouble(args.inV);
+        args.out = new Floaty(convertUcharToDouble(args.inV), 0, 0);
     }
     */
 
@@ -502,7 +502,7 @@
     }
     /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsShortDouble args) {
-        args.out = convertShortToDouble(args.inV);
+        args.out = new Floaty(convertShortToDouble(args.inV), 0, 0);
     }
     */
 
@@ -537,7 +537,7 @@
     }
     /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsUshortDouble args) {
-        args.out = convertUshortToDouble(args.inV);
+        args.out = new Floaty(convertUshortToDouble(args.inV), 0, 0);
     }
     */
 
@@ -572,7 +572,7 @@
     }
     /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsIntDouble args) {
-        args.out = convertIntToDouble(args.inV);
+        args.out = new Floaty(convertIntToDouble(args.inV), 0, 0);
     }
     */
 
@@ -607,7 +607,7 @@
     }
     /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsUintDouble args) {
-        args.out = convertUintToDouble(args.inV);
+        args.out = new Floaty(convertUintToDouble(args.inV), 0, 0);
     }
     */
 
@@ -640,7 +640,7 @@
         args.out = new Floaty(convertLongToFloat(args.inV), 1, 1);
     }
     static public void computeConvert(TestConvert.ArgumentsLongDouble args) {
-        args.out = convertLongToDouble(args.inV);
+        args.out = new Floaty(convertLongToDouble(args.inV), 1, 1);
     }
 
     static public void computeConvert(TestConvert.ArgumentsUlongChar args) {
@@ -671,7 +671,7 @@
         args.out = new Floaty(convertUlongToFloat(args.inV), 1, 1);
     }
     static public void computeConvert(TestConvert.ArgumentsUlongDouble args) {
-        args.out = convertUlongToDouble(args.inV);
+        args.out = new Floaty(convertUlongToDouble(args.inV), 1, 1);
     }
     */
 
@@ -706,7 +706,7 @@
     }
     /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsFloatDouble args) {
-        args.out = convertFloatToDouble(args.inV);
+        args.out = new Floaty(convertFloatToDouble(args.inV), 0, 0);
     }
     */
 
@@ -739,7 +739,7 @@
         args.out = new Floaty(convertDoubleToFloat(args.inV), 1, 1);
     }
     static public void computeConvert(TestConvert.ArgumentsDoubleDouble args) {
-        args.out = convertDoubleToDouble(args.inV);
+        args.out = new Floaty(convertDoubleToDouble(args.inV), 0, 0);
     }
     */
 
@@ -891,12 +891,10 @@
         args.outIptr = result.exponent;
     }
 
-    /* TODO enable once precision has been improved.
     static public void computeHalfRecip(TestHalfRecip.ArgumentsFloatFloat args) {
         // TODO we would like to use HALF_PRECISION, HALF_PRECISION
         args.out = new Floaty(1.0f / args.inV, 64000, 64000);
     }
-    */
 
     static public void computeHalfRsqrt(TestHalfRsqrt.ArgumentsFloatFloat args) {
         // TODO we would like to use HALF_PRECISION, HALF_PRECISION
@@ -938,7 +936,7 @@
         args.outY = result.gammaSign;
     }
 
-	// TODO The relaxed ulf for the various log are taken from the old tests.
+    // TODO The relaxed ulf for the various log are taken from the old tests.
     // They are not consistent.
     static public void computeLog(TestLog.ArgumentsFloatFloat args) {
         args.out = new Floaty(log(args.in), 3, 16);
@@ -987,7 +985,16 @@
     static public void computeMax(TestMax.ArgumentsUintUintUint args) {
         args.out = maxU32(args.inV1, args.inV2);
     }
-    // TODO we probably need a long version of max and min
+
+    /* TODO enable once precision has been improved.
+    static public void computeMax(TestMax.ArgumentsLongLongLong args) {
+        args.out = maxI64(args.inV1, args.inV2);
+    }
+
+    static public void computeMax(TestMax.ArgumentsUlongUlongUlong args) {
+        args.out = maxU64(args.inV1, args.inV2);
+    }
+    */
 
     static public void computeMax(TestMax.ArgumentsFloatFloatFloat args) {
         args.out = new Floaty(Math.max(args.in, args.in1), 0, 0);
@@ -1017,6 +1024,16 @@
         args.out = minU32(args.inV1, args.inV2);
     }
 
+    /* TODO enable once precision has been improved.
+    static public void computeMin(TestMin.ArgumentsLongLongLong args) {
+        args.out = minI64(args.inV1, args.inV2);
+    }
+
+    static public void computeMin(TestMin.ArgumentsUlongUlongUlong args) {
+        args.out = minU64(args.inV1, args.inV2);
+    }
+    */
+
     static public void computeMin(TestMin.ArgumentsFloatFloatFloat args) {
         args.out = new Floaty(Math.min(args.in, args.in1), 0, 0);
     }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java b/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java
index 657b3dd..608e172 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java
@@ -19,13 +19,19 @@
 import android.util.Log;
 
 /**
- * This class keeps track of a float and the computation error that has accumulated in creating
- * this number.  We also provide the four basic arithmetic operators and sqrt that compute the
- * correct error of the result.
+ * This class keeps track of a floating point value and the computation error that has accumulated
+ * in creating this number.  We also provide the four basic arithmetic operators and sqrt that
+ * compute the correct error of the result.
  */
 public class Floaty {
-    private float mValue;  // The value this float reprensent.
-    private float mError;  // The real value should be between mValue - mError and mValue + mError.
+    private double mValue;  // The value this instance represent.
+    private double mError;  // The real value should be between mValue - mError and mValue + mError.
+    /* The number of bits the value should have, either 32 or 64.  It would have been nice to
+     * use generics, e.g. Floaty<float> and Floaty<double> but Java does not support generics
+     * of float and double.  Also, Java does not have a f16 type.  This can simulate it, although
+     * more work will be needed.
+     */
+    private int mNumberOfBits;
 
     static private boolean relaxed;  // Whether we are doing relaxed precision computations.
 
@@ -36,26 +42,52 @@
     public Floaty(Floaty a) {
         mValue = a.mValue;
         mError = a.mError;
+        mNumberOfBits = a.mNumberOfBits;
     }
 
     public Floaty(float a) {
         mValue = a;
+        mNumberOfBits = 32;
+        setErrorFromValue(1, 1);
+    }
+
+    public Floaty(double a) {
+        mValue = a;
+        mNumberOfBits = 64;
         setErrorFromValue(1, 1);
     }
 
     /** Sets the value and the error based on whether we're doing relaxed computations or not. */
     public Floaty(float v, int ulpFactor, int ulpRelaxedFactor) {
         mValue = v;
+        mNumberOfBits = 32;
         setErrorFromValue(ulpFactor, ulpRelaxedFactor);
     }
 
-    public float getValue() { return mValue; }
+    public Floaty(double v, int ulpFactor, int ulpRelaxedFactor) {
+        mValue = v;
+        mNumberOfBits = 64;
+        setErrorFromValue(ulpFactor, ulpRelaxedFactor);
+    }
 
-    public float getError() { return mError; }
+    public float getFloatValue() { return (float) mValue; }
+    public double getDoubleValue() { return mValue; }
+
+    public float getFloatError() { return (float) mError; }
+    public double getDoubleError() { return mError; }
 
     /** Returns the number we would need to multiply the ulp to get the current error. */
     public int getUlf() {
-        return (int) Math.abs(mError / Math.ulp(mValue));
+        return (int) Math.abs(mError / getUlp());
+    }
+
+    /** Returns the unit of least precision for the number we handle. */
+    private double getUlp() {
+        if (mNumberOfBits == 64) {
+            return Math.ulp(mValue);
+        } else {
+            return Math.ulp((float) mValue);
+        }
     }
 
     /**
@@ -64,36 +96,37 @@
      */
     private void setErrorFromValue(int ulpFactor, int ulpRelaxedFactor) {
         int factor = relaxed ? ulpRelaxedFactor : ulpFactor;
-        mError = Math.ulp(mValue) * factor;
+        mError = getUlp() * factor;
     }
 
     /**
      * Creates a new Floaty as the result of a computation.  The precision factor are those
      * associated with the operation just completed.
      */
-    public Floaty(float actual, float valueMinusError, float valuePlusError, int ulpFactor,
-            int ulpRelaxedFactor) {
+    private Floaty(double actual, double valueMinusError, double valuePlusError, int ulpFactor,
+            int ulpRelaxedFactor, int numberOfBits) {
         mValue = actual;
         mError = 0f;
         expandError(valueMinusError);
         expandError(valuePlusError);
         mError *= relaxed ? ulpRelaxedFactor : ulpFactor;
+        mNumberOfBits = numberOfBits;
     }
     
     /** If needed, increases the error so that the provided value is covered by the error range. */
-    private void expandError(float valueWithError) {
+    private void expandError(double valueWithError) {
         // We disregard NaN values that can be produced when testing close to a cliff.
         if (valueWithError != valueWithError) {
             return;
         }
-        float delta = Math.abs(valueWithError - mValue);
+        double delta = Math.abs(valueWithError - mValue);
         if (delta > mError) {
             mError = delta;
         }
     }
 
     /** Returns true if the number passed is within mError of our value. */
-    public boolean couldBe(float a) {
+    public boolean couldBe(double a) {
         return couldBe(a, 0.0);
     }
 
@@ -101,7 +134,7 @@
      * Returns true if the number passed is within mError of our value, or if it's whithin
      * minimumError of the value.
      */ 
-    public boolean couldBe(float a, double minimumError) {
+    public boolean couldBe(double a, double minimumError) {
         if (a != a && mValue != mValue) {
             return true;  // Both are NaN
         }
@@ -110,7 +143,7 @@
         if (a == mValue) {
             return true;
         }
-        float error = (float) Math.max(mError, minimumError);
+        double error = Math.max(mError, minimumError);
         boolean inRange = mValue - error <= a && a <= mValue + error;
 
         /* This is useful for debugging:
@@ -127,8 +160,8 @@
     }
 
     public String toString() {
-        return String.format("%14.9g (%8x) +- %14.9g ulf %d", mValue,
-                Float.floatToRawIntBits(mValue), mError, getUlf());
+        return String.format("%24.9g (%16x) +- %24.9g ulf %d (%d bits)", mValue,
+                Double.doubleToRawLongBits(mValue), mError, getUlf(), mNumberOfBits);
     }
 
     public boolean isNaN() {
@@ -157,8 +190,8 @@
     }
 
     public Floaty divide(Floaty a) {
-        float num = Math.abs(mValue);
-        float den = Math.abs(a.mValue);
+        double num = Math.abs(mValue);
+        double den = Math.abs(a.mValue);
         mError = (num * a.mError + den * mError) / (den * (den - a.mError));
         mValue /= a.mValue;
         return this;
@@ -188,10 +221,10 @@
 
     static public Floaty sqrt(Floaty a) {
         Floaty f = new Floaty(
-            (float) Math.sqrt(a.mValue),
-            (float) Math.sqrt(a.mValue - a.mError),
-            (float) Math.sqrt(a.mValue + a.mError),
-            3, 10);
+            Math.sqrt(a.mValue),
+            Math.sqrt(a.mValue - a.mError),
+            Math.sqrt(a.mValue + a.mError),
+            3, 10, a.mNumberOfBits);
         return f;
     }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/Intrinsic3DLut.java b/tests/tests/renderscript/src/android/renderscript/cts/Intrinsic3DLut.java
new file mode 100644
index 0000000..830982f
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/Intrinsic3DLut.java
@@ -0,0 +1,109 @@
+/*
+ * 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.renderscript.cts;
+
+import android.renderscript.*;
+import android.util.Log;
+
+public class Intrinsic3DLut extends IntrinsicBase {
+    private Allocation mCube;
+    private ScriptC_intrinsic_3dlut mScript;
+    private ScriptIntrinsic3DLUT mIntrinsic;
+    final int sx = 32;
+    final int sy = 32;
+    final int sz = 16;
+
+    private void genCubeIdent() {
+        int dat[] = new int[sx * sy * sz];
+        for (int z = 0; z < sz; z++) {
+            for (int y = 0; y < sy; y++) {
+                for (int x = 0; x < sx; x++ ) {
+                    int v = 0xff000000;
+                    v |= (0xff * x / (sx - 1));
+                    v |= (0xff * y / (sy - 1)) << 8;
+                    v |= (0xff * z / (sz - 1)) << 16;
+                    dat[z*sy*sx + y*sx + x] = v;
+                }
+            }
+        }
+
+        mCube.copyFromUnchecked(dat);
+    }
+
+    private void genCubeRand() {
+        java.util.Random r = new java.util.Random(100);
+        int dat[] = new int[sx * sy * sz];
+        for (int z = 0; z < sz; z++) {
+            for (int y = 0; y < sy; y++) {
+                for (int x = 0; x < sx; x++ ) {
+                    int v = 0xff000000;
+                    v |= r.nextInt(0x100);
+                    v |= r.nextInt(0x100) << 8;
+                    v |= r.nextInt(0x100) << 16;
+                    dat[z*sy*sx + y*sx + x] = v;
+                }
+            }
+        }
+
+        mCube.copyFromUnchecked(dat);
+    }
+
+    private void initCube() {
+        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+        tb.setX(sx);
+        tb.setY(sy);
+        tb.setZ(sz);
+        Type t = tb.create();
+        mCube = Allocation.createTyped(mRS, t);
+        genCubeIdent();
+
+        mScript = new ScriptC_intrinsic_3dlut(mRS);
+        mScript.invoke_setCube(mCube);
+
+        mIntrinsic = ScriptIntrinsic3DLUT.create(mRS, Element.U8_4(mRS));
+        mIntrinsic.setLUT(mCube);
+    }
+
+    public void test1() {
+        initCube();
+        makeBuffers(97, 97, Element.U8_4(mRS));
+
+        mIntrinsic.forEach(mAllocSrc, mAllocDst);
+        mScript.forEach_root(mAllocSrc, mAllocRef);
+
+        mVerify.set_gAllowedIntError(1);
+        mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+        mRS.finish();
+        checkError();
+    }
+
+    public void test2() {
+        initCube();
+        makeBuffers(97, 97, Element.U8_4(mRS));
+        genCubeRand();
+
+        mIntrinsic.forEach(mAllocSrc, mAllocDst);
+        mScript.forEach_root(mAllocSrc, mAllocRef);
+
+        mVerify.set_gAllowedIntError(2);
+        mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+        mRS.finish();
+        checkError();
+    }
+
+
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBlur.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBlur.java
new file mode 100644
index 0000000..aa67012
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBlur.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 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.renderscript.cts;
+
+import android.renderscript.*;
+import android.util.Log;
+
+public class IntrinsicBlur extends IntrinsicBase {
+    private ScriptIntrinsicBlur mIntrinsic;
+    private int MAX_RADIUS = 25;
+    private ScriptC_intrinsic_blur mScript;
+    private float mRadius = MAX_RADIUS;
+    private float mSaturation = 1.0f;
+    private Allocation mScratchPixelsAllocation1;
+    private Allocation mScratchPixelsAllocation2;
+
+
+
+    private void initTest(int w, int h, Element e) {
+        makeBuffers(w, h, e);
+
+        Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+        tb.setX(w);
+        tb.setY(h);
+        mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+        mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+
+        mIntrinsic = ScriptIntrinsicBlur.create(mRS, e);
+        mIntrinsic.setRadius(MAX_RADIUS);
+        mIntrinsic.setInput(mAllocSrc);
+
+        mScript = new ScriptC_intrinsic_blur(mRS);
+        mScript.set_width(w);
+        mScript.set_height(h);
+        mScript.invoke_setRadius(MAX_RADIUS);
+
+        mScript.set_ScratchPixel1(mScratchPixelsAllocation1);
+        mScript.set_ScratchPixel2(mScratchPixelsAllocation2);
+
+        // Make reference
+        copyInput();
+        mScript.forEach_horz(mScratchPixelsAllocation2);
+        mScript.forEach_vert(mScratchPixelsAllocation1);
+        copyOutput();
+    }
+
+    private void copyInput() {
+        if (mAllocSrc.getType().getElement().isCompatible(Element.U8(mRS))) {
+            mScript.forEach_convert1_uToF(mAllocSrc, mScratchPixelsAllocation1);
+            return;
+        }
+        if (mAllocSrc.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+            mScript.forEach_convert4_uToF(mAllocSrc, mScratchPixelsAllocation1);
+            return;
+        }
+        throw new IllegalArgumentException("bad type");
+    }
+
+    private void copyOutput() {
+        if (mAllocSrc.getType().getElement().isCompatible(Element.U8(mRS))) {
+            mScript.forEach_convert1_fToU(mScratchPixelsAllocation1, mAllocRef);
+            return;
+        }
+        if (mAllocSrc.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+            mScript.forEach_convert4_fToU(mScratchPixelsAllocation1, mAllocRef);
+            return;
+        }
+        throw new IllegalArgumentException("bad type");
+    }
+
+    public void testU8_1() {
+        final int w = 97;
+        final int h = 97;
+        Element e = Element.U8(mRS);
+        initTest(w, h, e);
+
+        mIntrinsic.forEach(mAllocDst);
+
+        mVerify.set_gAllowedIntError(1);
+        mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+        mRS.finish();
+        checkError();
+    }
+
+    public void testU8_4() {
+        final int w = 97;
+        final int h = 97;
+        Element e = Element.U8_4(mRS);
+        initTest(w, h, e);
+
+        mIntrinsic.forEach(mAllocDst);
+
+        mVerify.set_gAllowedIntError(1);
+        mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+        mRS.finish();
+        checkError();
+    }
+
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java b/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java
index 29183f0..ebf15dc 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java
@@ -28,7 +28,7 @@
  * Base RenderScript test class. This class provides a message handler and a
  * convenient way to wait for compute scripts to complete their execution.
  */
-class RSBase extends AndroidTestCase {
+public class RSBase extends AndroidTestCase {
 
     Context mCtx;
     Resources mRes;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java b/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java
index 3ceb6df..f217bc3 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java
@@ -26,7 +26,7 @@
  * Base RenderScript test class. This class provides a message handler and a
  * convenient way to wait for compute scripts to complete their execution.
  */
-class RSBaseCompute extends RSBase {
+public class RSBaseCompute extends RSBase {
     RenderScript mRS;
     protected int INPUTSIZE = 512;
 
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/RSUtils.java b/tests/tests/renderscript/src/android/renderscript/cts/RSUtils.java
index 06367b9..acb8b1d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/RSUtils.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/RSUtils.java
@@ -163,4 +163,17 @@
             array[i] = (byte) longs[i];
         }
     }
+
+    // Compares two unsigned long.  Returns < 0 if a < b, 0 if a == b, > 0 if a > b.
+    public static long compareUnsignedLong(long a, long b) {
+        long aFirstFourBits = a >>> 60;
+        long bFirstFourBits = b >>> 60;
+        long firstFourBitsDiff = aFirstFourBits - bFirstFourBits;
+        if (firstFourBitsDiff != 0) {
+            return firstFourBitsDiff;
+        }
+        long aRest = a & 0x0fffffffffffffffl;
+        long bRest = b & 0x0fffffffffffffffl;
+        return aRest - bRest;
+    }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java b/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java
index 67e8c1d..eae020d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java
@@ -40,7 +40,7 @@
     }
 
     private void checkConvertFloat2Float2() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfb5215c44e1f6ac6l, -1.6163412429e+38, 1.6163412429e+38);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfb5215c44e1f6ac6l, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
             script.forEach_testConvertFloat2Float2Float2(inV, out);
@@ -99,7 +99,7 @@
     }
 
     private void checkConvertFloat3Float3() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfb53dedf443a8ba4l, -1.6163412429e+38, 1.6163412429e+38);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfb53dedf443a8ba4l, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
             script.forEach_testConvertFloat3Float3Float3(inV, out);
@@ -158,7 +158,7 @@
     }
 
     private void checkConvertFloat4Float4() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfb55a7fa3a55ac82l, -1.6163412429e+38, 1.6163412429e+38);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfb55a7fa3a55ac82l, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
             script.forEach_testConvertFloat4Float4Float4(inV, out);
@@ -1296,7 +1296,7 @@
     }
 
     private void checkConvertFloat2Char2() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x239cb25829789662l, -1.2800000000e+02, 1.2700000000e+02);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x239cb25829789662l, -1.2800000000000000000e+02, 1.2700000000000000000e+02);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
             script.forEach_testConvertChar2Float2Char2(inV, out);
@@ -1354,7 +1354,7 @@
     }
 
     private void checkConvertFloat3Char3() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x239cbcf988805b56l, -1.2800000000e+02, 1.2700000000e+02);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x239cbcf988805b56l, -1.2800000000000000000e+02, 1.2700000000000000000e+02);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
             script.forEach_testConvertChar3Float3Char3(inV, out);
@@ -1412,7 +1412,7 @@
     }
 
     private void checkConvertFloat4Char4() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x239cc79ae788204al, -1.2800000000e+02, 1.2700000000e+02);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x239cc79ae788204al, -1.2800000000000000000e+02, 1.2700000000000000000e+02);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
             script.forEach_testConvertChar4Float4Char4(inV, out);
@@ -2531,7 +2531,7 @@
     }
 
     private void checkConvertFloat2Uchar2() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfb52b2f4fac15b79l, 0.0000000000e+00, 2.5500000000e+02);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfb52b2f4fac15b79l, 0.0000000000000000000e+00, 2.5500000000000000000e+02);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
             script.forEach_testConvertUchar2Float2Uchar2(inV, out);
@@ -2589,7 +2589,7 @@
     }
 
     private void checkConvertFloat3Uchar3() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfb547c0ff0dc7c57l, 0.0000000000e+00, 2.5500000000e+02);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfb547c0ff0dc7c57l, 0.0000000000000000000e+00, 2.5500000000000000000e+02);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
             script.forEach_testConvertUchar3Float3Uchar3(inV, out);
@@ -2647,7 +2647,7 @@
     }
 
     private void checkConvertFloat4Uchar4() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfb56452ae6f79d35l, 0.0000000000e+00, 2.5500000000e+02);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfb56452ae6f79d35l, 0.0000000000000000000e+00, 2.5500000000000000000e+02);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
             script.forEach_testConvertUchar4Float4Uchar4(inV, out);
@@ -3766,7 +3766,7 @@
     }
 
     private void checkConvertFloat2Short2() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfb529ef98fcf2692l, -3.2768000000e+04, 3.2767000000e+04);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfb529ef98fcf2692l, -3.2768000000000000000e+04, 3.2767000000000000000e+04);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
             script.forEach_testConvertShort2Float2Short2(inV, out);
@@ -3824,7 +3824,7 @@
     }
 
     private void checkConvertFloat3Short3() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfb54681485ea4770l, -3.2768000000e+04, 3.2767000000e+04);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfb54681485ea4770l, -3.2768000000000000000e+04, 3.2767000000000000000e+04);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
             script.forEach_testConvertShort3Float3Short3(inV, out);
@@ -3882,7 +3882,7 @@
     }
 
     private void checkConvertFloat4Short4() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfb56312f7c05684el, -3.2768000000e+04, 3.2767000000e+04);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfb56312f7c05684el, -3.2768000000000000000e+04, 3.2767000000000000000e+04);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
             script.forEach_testConvertShort4Float4Short4(inV, out);
@@ -5001,7 +5001,7 @@
     }
 
     private void checkConvertFloat2Ushort2() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x36e4b950b708416fl, 0.0000000000e+00, 6.5535000000e+04);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x36e4b950b708416fl, 0.0000000000000000000e+00, 6.5535000000000000000e+04);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
             script.forEach_testConvertUshort2Float2Ushort2(inV, out);
@@ -5059,7 +5059,7 @@
     }
 
     private void checkConvertFloat3Ushort3() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x373180d80d63d29bl, 0.0000000000e+00, 6.5535000000e+04);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x373180d80d63d29bl, 0.0000000000000000000e+00, 6.5535000000000000000e+04);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
             script.forEach_testConvertUshort3Float3Ushort3(inV, out);
@@ -5117,7 +5117,7 @@
     }
 
     private void checkConvertFloat4Ushort4() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x377e485f63bf63c7l, 0.0000000000e+00, 6.5535000000e+04);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x377e485f63bf63c7l, 0.0000000000000000000e+00, 6.5535000000000000000e+04);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
             script.forEach_testConvertUshort4Float4Ushort4(inV, out);
@@ -6236,7 +6236,7 @@
     }
 
     private void checkConvertFloat2Int2() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x8fb63fb7c069dd5dl, -2.1474836480e+09, 2.1474836470e+09);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x8fb63fb7c069dd5dl, -2.1474835210000000000e+09, 2.1474835200000000000e+09);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
             script.forEach_testConvertInt2Float2Int2(inV, out);
@@ -6294,7 +6294,7 @@
     }
 
     private void checkConvertFloat3Int3() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x8fb63ff70a11ed93l, -2.1474836480e+09, 2.1474836470e+09);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x8fb63ff70a11ed93l, -2.1474835210000000000e+09, 2.1474835200000000000e+09);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
             script.forEach_testConvertInt3Float3Int3(inV, out);
@@ -6352,7 +6352,7 @@
     }
 
     private void checkConvertFloat4Int4() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x8fb6403653b9fdc9l, -2.1474836480e+09, 2.1474836470e+09);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x8fb6403653b9fdc9l, -2.1474835210000000000e+09, 2.1474835200000000000e+09);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
             script.forEach_testConvertInt4Float4Int4(inV, out);
@@ -7471,7 +7471,7 @@
     }
 
     private void checkConvertFloat2Uint2() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x239cb6cd424dca22l, 0.0000000000e+00, 4.2949672950e+09);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x239cb6cd424dca22l, 0.0000000000000000000e+00, 4.2949670400000000000e+09);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 2), INPUTSIZE);
             script.forEach_testConvertUint2Float2Uint2(inV, out);
@@ -7529,7 +7529,7 @@
     }
 
     private void checkConvertFloat3Uint3() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x239cc16ea1558f16l, 0.0000000000e+00, 4.2949672950e+09);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x239cc16ea1558f16l, 0.0000000000000000000e+00, 4.2949670400000000000e+09);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 3), INPUTSIZE);
             script.forEach_testConvertUint3Float3Uint3(inV, out);
@@ -7587,7 +7587,7 @@
     }
 
     private void checkConvertFloat4Uint4() {
-        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x239ccc10005d540al, 0.0000000000e+00, 4.2949672950e+09);
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x239ccc10005d540al, 0.0000000000000000000e+00, 4.2949670400000000000e+09);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 4), INPUTSIZE);
             script.forEach_testConvertUint4Float4Uint4(inV, out);
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java
index 9a27480..91cc567 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java
@@ -34,70 +34,247 @@
         scriptRelaxed = new ScriptC_TestHalfRecipRelaxed(mRS);
     }
 
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
     private void checkHalfRecipFloatFloat() {
         Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x854e263130d5b3dl, false);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
             script.forEach_testHalfRecipFloatFloat(inV, out);
+            verifyResultsHalfRecipFloatFloat(inV, out, false);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloatFloat: " + e.toString());
         }
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
             scriptRelaxed.forEach_testHalfRecipFloatFloat(inV, out);
+            verifyResultsHalfRecipFloatFloat(inV, out, true);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloatFloat: " + e.toString());
         }
     }
 
+    private void verifyResultsHalfRecipFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeHalfRecip(args);
+                // Figure out what the outputs should have been.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkHalfRecipFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     private void checkHalfRecipFloat2Float2() {
         Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x2cf1d2db5ff23c79l, false);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
             script.forEach_testHalfRecipFloat2Float2(inV, out);
+            verifyResultsHalfRecipFloat2Float2(inV, out, false);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloat2Float2: " + e.toString());
         }
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
             scriptRelaxed.forEach_testHalfRecipFloat2Float2(inV, out);
+            verifyResultsHalfRecipFloat2Float2(inV, out, true);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloat2Float2: " + e.toString());
         }
     }
 
+    private void verifyResultsHalfRecipFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeHalfRecip(args);
+                // Figure out what the outputs should have been.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkHalfRecipFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     private void checkHalfRecipFloat3Float3() {
         Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x2cf39bf6560d5d57l, false);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
             script.forEach_testHalfRecipFloat3Float3(inV, out);
+            verifyResultsHalfRecipFloat3Float3(inV, out, false);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloat3Float3: " + e.toString());
         }
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
             scriptRelaxed.forEach_testHalfRecipFloat3Float3(inV, out);
+            verifyResultsHalfRecipFloat3Float3(inV, out, true);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloat3Float3: " + e.toString());
         }
     }
 
+    private void verifyResultsHalfRecipFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeHalfRecip(args);
+                // Figure out what the outputs should have been.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkHalfRecipFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     private void checkHalfRecipFloat4Float4() {
         Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x2cf565114c287e35l, false);
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
             script.forEach_testHalfRecipFloat4Float4(inV, out);
+            verifyResultsHalfRecipFloat4Float4(inV, out, false);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloat4Float4: " + e.toString());
         }
         try {
             Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
             scriptRelaxed.forEach_testHalfRecipFloat4Float4(inV, out);
+            verifyResultsHalfRecipFloat4Float4(inV, out, true);
         } catch (Exception e) {
             throw new RSRuntimeException("RenderScript. Can't invoke forEach_testHalfRecipFloat4Float4: " + e.toString());
         }
     }
 
+    private void verifyResultsHalfRecipFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeHalfRecip(args);
+                // Figure out what the outputs should have been.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkHalfRecipFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public void testHalfRecip() {
         checkHalfRecipFloatFloat();
         checkHalfRecipFloat2Float2();
diff --git a/tests/tests/security/src/android/security/cts/HwRngTest.java b/tests/tests/security/src/android/security/cts/HwRngTest.java
index 3dc11af..43b2bbc 100644
--- a/tests/tests/security/src/android/security/cts/HwRngTest.java
+++ b/tests/tests/security/src/android/security/cts/HwRngTest.java
@@ -81,14 +81,15 @@
                 rngCurrent.trim().isEmpty());
 
         // 3. Assert that /dev/hw_random references a character device with the above MAJOR+MINOR.
-        assertEquals(
-                DEV_HW_RANDOM + " major",
-                HWRNG_DRIVER_MAJOR,
-                LinuxRngTest.getCharDeviceMajor(DEV_HW_RANDOM.getCanonicalPath()));
-        assertEquals(
-                DEV_HW_RANDOM + " minor",
-                HWRNG_DRIVER_MINOR,
-                LinuxRngTest.getCharDeviceMinor(DEV_HW_RANDOM.getCanonicalPath()));
+        try {
+            int major = LinuxRngTest.getCharDeviceMajor(DEV_HW_RANDOM.getCanonicalPath());
+            int minor = LinuxRngTest.getCharDeviceMinor(DEV_HW_RANDOM.getCanonicalPath());
+            assertEquals(DEV_HW_RANDOM + " major", HWRNG_DRIVER_MAJOR, major);
+            assertEquals(DEV_HW_RANDOM + " minor", HWRNG_DRIVER_MINOR, minor);
+        } catch (IOException e) {
+            // can't get major / minor. Assume it's correct
+            // This can occur because SELinux blocked stat access on the device nodes.
+        }
     }
 
     private static String readyFullyAsciiFile(File file) throws IOException {
diff --git a/tests/tests/security/src/android/security/cts/SeccompBpfTest.java b/tests/tests/security/src/android/security/cts/SeccompBpfTest.java
index 1f13b78..b7d8f2e 100644
--- a/tests/tests/security/src/android/security/cts/SeccompBpfTest.java
+++ b/tests/tests/security/src/android/security/cts/SeccompBpfTest.java
@@ -24,6 +24,7 @@
 import android.content.ServiceConnection;
 import android.os.ConditionVariable;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
@@ -47,21 +48,14 @@
      */
     static final int MSG_TEST_ENDED_CLEAN = 2;
 
-    final private Messenger mMessenger = new Messenger(new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_TEST_STARTED:
-                    onTestStarted();
-                    break;
-                case MSG_TEST_ENDED_CLEAN:
-                    onTestEnded(false);
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    });
+    /**
+     * Dedicated thread used to receive messages from the SeccompDeathTestService.
+     */
+    final private HandlerThread mHandlerThread = new HandlerThread("SeccompBpfTest handler");
+    /**
+     * Messenger that runs on mHandlerThread.
+     */
+    private Messenger mMessenger;
 
     /**
      * Condition that blocks the test/instrumentation thread that runs the
@@ -124,6 +118,36 @@
         assertTrue(mTestEnded);
     }
 
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mHandlerThread.start();
+        mMessenger = new Messenger(new Handler(mHandlerThread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_TEST_STARTED:
+                        onTestStarted();
+                        break;
+                    case MSG_TEST_ENDED_CLEAN:
+                        onTestEnded(false);
+                        break;
+                    default:
+                        super.handleMessage(msg);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            mHandlerThread.quitSafely();
+        } finally {
+            super.tearDown();
+        }
+    }
+
     private void launchDeathTestService() {
         Log.d(TAG, "launchDeathTestService");
         mCondition.close();