Add CtsVerifier tests for:
- Magnetometer
- Accelerometer
- Gyroscope

Add infrastructure to add semi-automated sensor tests to CtsVerifier
Refactors out sensor helpers into a static java library

b/11277430

Change-Id: Ifb4978b741ec46f3335e4d7a471fa425f83a4bba
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 834320b..397b39a 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -23,6 +23,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_STATIC_JAVA_LIBRARIES := cts-sensors-tests
+
 LOCAL_PACKAGE_NAME := CtsVerifier
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 38b6412..d0fd269 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -76,7 +76,7 @@
                 android:configChanges="keyboardHidden|orientation|screenSize"
                 android:label="@string/report_viewer" />
 
-        <provider android:name=".TestResultsProvider" 
+        <provider android:name=".TestResultsProvider"
                 android:authorities="com.android.cts.verifier.testresultsprovider" />
 
         <activity android:name=".admin.PolicySerializationTestActivity"
@@ -417,6 +417,18 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.sensor.accelerometer" />
         </activity>
 
+        <activity android:name=".sensors.AccelerometerMeasurementTestActivity"
+                  android:label="@string/snsr_accel_m_test"
+                  android:screenOrientation="nosensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_sensors"/>
+            <meta-data android:name="test_required_features"
+                       android:value="android.hardware.sensor.accelerometer"/>
+        </activity>
+
         <activity android:name=".sensors.GyroscopeTestActivity" android:label="@string/snsr_gyro_test"
                 android:screenOrientation="nosensor">
             <intent-filter>
@@ -427,6 +439,30 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.sensor.gyroscope" />
         </activity>
 
+        <activity android:name=".sensors.GyroscopeMeasurementTestActivity"
+                  android:label="@string/snsr_gyro_m_test"
+                  android:screenOrientation="nosensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_sensors"/>
+            <meta-data android:name="test_require_features"
+                       android:value="android.hardware.sensor.gyroscope"/>
+        </activity>
+
+        <activity android:name=".sensors.MagneticFieldMeasurementTestActivity"
+                  android:label="@string/snsr_mag_m_test"
+                  android:screenOrientation="nosensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
+            <meta-data android:name="test_required_features"
+                       android:value="android.hardware.sensor.compass" />
+        </activity>
+
         <activity android:name=".camera.formats.CameraFormatsActivity"
                  android:label="@string/camera_format"
                  android:screenOrientation="landscape">
@@ -524,7 +560,7 @@
             <meta-data android:name="test_category" android:value="@string/test_category_networking" />
             <meta-data android:name="test_required_features" android:value="android.hardware.wifi.direct" />
         </activity>
-        
+
         <activity android:name=".nls.NotificationListenerVerifierActivity"
                 android:label="@string/nls_test">
             <intent-filter>
@@ -542,7 +578,7 @@
                 <action android:name="android.service.notification.NotificationListenerService" />
             </intent-filter>
         </service>
-        
+
         <service  android:name="nls.NotificationListenerVerifierActivity$DismissService"/>
         <activity android:name=".security.CAInstallNotificationVerifierActivity"
                 android:label="@string/cacert_test">
diff --git a/apps/CtsVerifier/proguard.flags b/apps/CtsVerifier/proguard.flags
index ddaf962..dde9fde 100644
--- a/apps/CtsVerifier/proguard.flags
+++ b/apps/CtsVerifier/proguard.flags
@@ -6,3 +6,5 @@
     private <fields>;
 }
 
+-dontwarn android.hardware.Sensor
+-dontwarn java.util.concurrent.ConcurrentLinkedDeque
diff --git a/apps/CtsVerifier/res/layout/snsr_semi_auto_test.xml b/apps/CtsVerifier/res/layout/snsr_semi_auto_test.xml
new file mode 100644
index 0000000..92039f0
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/snsr_semi_auto_test.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <TextView android:id="@+id/log_text"
+              android:gravity="bottom"
+              android:layout_height="wrap_content"
+              android:layout_width="wrap_content"
+              android:maxLines="27"
+              android:paddingBottom="5dip"
+              android:paddingLeft="10dip"
+              android:paddingRight="10dip"
+              android:paddingTop="5dip"
+              android:scrollbars="vertical"
+            />
+
+    <Button android:id="@+id/next_button"
+            android:layout_alignParentBottom="true"
+            android:layout_centerInParent="true"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="20dip"
+            android:layout_width="120dip"
+            android:text="@string/next_button_text"
+            android:textSize="24dip"
+            />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 503367e..912c397 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -21,6 +21,7 @@
     <string name="pass_button_text">Pass</string>
     <string name="info_button_text">Info</string>
     <string name="fail_button_text">Fail</string>
+    <string name="next_button_text">Next</string>
 
     <!-- Strings for TestListActivity -->
     <string name="test_category_audio">Audio</string>
@@ -129,16 +130,16 @@
     <string name="bt_connection_access_client">Connection Access Client</string>
     <string name="bt_connection_access_server_info">
         Start the CTS Verifier on another device, start the Bluetooth test, and choose
-        \"Connection Access Client\" to setup the test. 
-        \n\nFirst, unpair the devices via Bluetooth settings. Then connect the devices together 
+        \"Connection Access Client\" to setup the test.
+        \n\nFirst, unpair the devices via Bluetooth settings. Then connect the devices together
         using the \"Make Discoverable\" and \"Pick Server\" buttons.
         \n\nA connection access request should appear on the server and enable the pass button.
     </string>
     <string name="bt_connection_access_client_info">
         Start the CTS Verifier on another device, start the Bluetooth test, and choose
-        \"Connection Access Server\" to complete the test. 
-        \n\nMake the device acting as the server discoverable and connect to it via the 
-        \"Pick Server\" button. Check that the server displays the connection access request 
+        \"Connection Access Server\" to complete the test.
+        \n\nMake the device acting as the server discoverable and connect to it via the
+        \"Pick Server\" button. Check that the server displays the connection access request
         dialog. The client device does not need to do anything else.
     </string>
     <string name="bt_ca_dialog">Was the connection access request dialog shown?</string>
@@ -211,7 +212,7 @@
     </string>
 
     <string name="nfc_not_enabled">NFC is not enabled!</string>
-    <string name="nfc_not_enabled_message">These tests require NFC to be enabled. Click the 
+    <string name="nfc_not_enabled_message">These tests require NFC to be enabled. Click the
         button below to goto Settings and enable it.</string>
     <string name="nfc_settings">NFC Settings</string>
 
@@ -241,7 +242,7 @@
         device running the \"CTS Verifier NDEF Sender\"...</string>
     <string name="nfc_ndef_push_receive_success">Successfully received the correct NDEF push
         message.</string>
-    <string name="nfc_ndef_push_receive_failure">Failed to receive the correct NDEF push 
+    <string name="nfc_ndef_push_receive_failure">Failed to receive the correct NDEF push
         message.</string>
 
     <string name="nfc_tag_verifier">NFC Tag Verifier</string>
@@ -343,10 +344,13 @@
     <string name="accessService">AccessService</string>
     <string name="offhostService">OffhostService</string>
 
-    <!-- Strings for AccelerometerTestActivity and GyroscopeTestActivity -->
+    <!-- Strings for Sensor Test Activities -->
+    <!-- Accelerometer -->
     <string name="snsr_accel_test">Accelerometer Test</string>
     <string name="snsr_accel_test_info">This test verifies that the accelerometer is working properly. As you move the device around through space, the triangle should always point down (i.e. in the direction of gravity.) If it does not, the accelerometer is improperly configured.</string>
+    <string name="snsr_accel_m_test">Accelerometer Measurement Tests</string>
 
+    <!-- Gyroscope -->
     <string name="snsr_gyro_test">Gyroscope Test</string>
     <string name="snsr_gyro_test_info">This test verifies that the gyroscope is working properly.\n\nRotate your device as shown by the 3D block. A green background or a check mark indicates that the gyroscope\'s value is correct. A red background or a X mark indicates that the gyroscope\'s value is not right.\n\nThere are 6 parts of the test corresponding to each rotation. Press Pass for all the stages to complete this test.</string>
     <string name="snsr_gyro_test_progress">Test %1$d of %2$d</string>
@@ -354,6 +358,10 @@
     <string name="snsr_gyro_test_no_gyro_message">It doesn\'t seem like you have a gyroscope, so you don\'t need to run this test.</string>
     <string name="snsr_gyro_test_degrees_title">Wrong units?</string>
     <string name="snsr_gyro_test_degrees_message">These values looks like degrees per second. These should be radians per second!</string>
+    <string name="snsr_gyro_m_test">Gyroscope Measurement Test</string>
+
+    <!-- Magnetic Field -->
+    <string name="snsr_mag_m_test">Magnetic Field Measurement Tests</string>
 
     <!-- Strings for SuidFilesActivity -->
     <string name="suid_files">SUID File Scanner</string>
@@ -475,7 +483,7 @@
     <!-- Strings for USB accessory test activity -->
     <string name="usb_accessory_test">USB Accessory Test</string>
     <string name="usb_accessory_test_info">
-        1. Connect your Android device to a computer and run the \'cts-usb-accessory\' program 
+        1. Connect your Android device to a computer and run the \'cts-usb-accessory\' program
         included with the CTS Verifier bundle.
         \n\n2. If you have not started the CTS Verifier, press \'OK\' when asked to open the CTS
         Verifier when the accessory is connected. \n\nIf you are already in this test,
@@ -673,9 +681,9 @@
         and disabled, and that once enabled the service is able to receive notificaitons and
         dismiss them.
     </string>
-    <string name="nls_enable_service">Please enable \"Notification Listener for CTS Verifier\" 
+    <string name="nls_enable_service">Please enable \"Notification Listener for CTS Verifier\"
         under Security > Notification Access and return here.</string>
-    <string name="nls_disable_service">Please disable \"Notification Listener for CTS Verifier\" 
+    <string name="nls_disable_service">Please disable \"Notification Listener for CTS Verifier\"
         under Security > Notification Access and return here.</string>
     <string name="nls_start_settings">Launch Settings</string>
     <string name="nls_service_started">Service should start once enabled.</string>
@@ -685,7 +693,7 @@
     <string name="nls_clear_all">Check that service can clear all notifications.</string>
     <string name="nls_service_stopped">Service should stop once disabled.</string>
     <string name="nls_note_missed">Check that notification was not received.</string>
-    
+
     <string name="cacert_test">CA Cert Notification Test</string>
     <string name="cacert_info">This test checks that when a CA Certificate is installed, the user is notified.</string>
     <string name="cacert_do_something">Do it</string>
@@ -764,7 +772,7 @@
            Repeating on: Monday and Wednesday. \n
     </string>
     <string name="dc_full_alarm_button">Create Alarm</string>
-    
+
     <string name="dc_set_timer_with_ui_test">Set Timer Test</string>
     <string name="dc_set_timer_with_ui_test_info">
         This test verifies that the ACTION_SET_TIMER API with no paramters open the UI\n
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
new file mode 100644
index 0000000..0ad86bc
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -0,0 +1,110 @@
+/*
+ * 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 com.android.cts.verifier.sensors;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.sensorTestOperations.VerifyMeasurementsOperation;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Semi-automated test that focuses on characteristics associated with Accelerometer measurements.
+ */
+public class AccelerometerMeasurementTestActivity extends BaseSensorSemiAutomatedTestActivity {
+    @Override
+    protected void onRun() throws Throwable {
+        verifyMeasurements(
+                "Place the device in a flat surface with the screen facing the ceiling",
+                0, 0, SensorManager.STANDARD_GRAVITY);
+
+        delayedVerifyMeasurements(
+                "Press 'Next' and place the device in a flat surface with the screen facing it",
+                0, 0, -SensorManager.STANDARD_GRAVITY);
+
+        verifyMeasurements(
+                "Place the device in a flat surface resting vertically on its right side",
+                -SensorManager.STANDARD_GRAVITY, 0, 0);
+
+        verifyMeasurements(
+                "Place the device in a flat surface resting vertically on its left side",
+                SensorManager.STANDARD_GRAVITY, 0, 0);
+
+        verifyMeasurements(
+                "Place the device in a flat surface resting vertically on its top side",
+                0, -SensorManager.STANDARD_GRAVITY, 0);
+
+        verifyMeasurements(
+                "Place the device in a flat surface resting vertically on its bottom side",
+                0, SensorManager.STANDARD_GRAVITY, 0);
+    }
+
+    /**
+     * This test verifies that the Accelerometer measurements are close to the expected reference
+     * values (range and scale).
+     *
+     * The test takes a set of samples from the sensor under test and calculates the mean of each
+     * axis that the sensor data collects. It then compares it against the test expectations that
+     * are represented by reference values and a threshold.
+
+     * The reference values are coupled to the orientation of the device. The test is susceptible to
+     * errors when the device is not oriented properly, or the units in which the data are reported
+     * and the expectations are set are different.
+     *
+     * The error message 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 mean of values sampled from the sensor
+     */
+    private void verifyMeasurements(double ... expectations) throws Throwable {
+        Thread.sleep(500 /*ms*/);
+        VerifyMeasurementsOperation verifyMeasurements = new VerifyMeasurementsOperation(
+                getApplicationContext(),
+                Sensor.TYPE_ACCELEROMETER,
+                SensorManager.SENSOR_DELAY_FASTEST,
+                0 /*reportLatencyInUs*/,
+                expectations,
+                0.7f /* m / s^2 */);
+        verifyMeasurements.execute();
+        logSuccess();
+    }
+
+    private void delayedVerifyMeasurements(
+            String message,
+            double ... expectations) throws Throwable {
+        appendText(String.format("\n%s.", message));
+        appendText("A sound will be played once the verification is complete...");
+        waitForUser();
+        Thread.sleep(TimeUnit.MILLISECONDS.convert(10, TimeUnit.SECONDS));
+
+        try {
+            verifyMeasurements(expectations);
+        } finally {
+            playSound();
+        }
+    }
+
+    private void verifyMeasurements(String message, double ... expectations) throws Throwable {
+        appendText(String.format("\n%s.", message));
+        appendText("Press 'Next' when ready and keep the device steady.");
+        waitForUser();
+
+        verifyMeasurements(expectations);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java
new file mode 100644
index 0000000..9f4b2b2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java
@@ -0,0 +1,221 @@
+/*
+ * 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 com.android.cts.verifier.sensors;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.media.MediaPlayer;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.method.ScrollingMovementMethod;
+import android.text.style.ForegroundColorSpan;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+import java.security.InvalidParameterException;
+import java.util.concurrent.Semaphore;
+
+/**
+ * Base class to author Sensor semi-automated test cases.
+ * These tests can only wait for operators to notify at some intervals, but the test needs to be
+ * autonomous to verify the data collected.
+ */
+public abstract class BaseSensorSemiAutomatedTestActivity
+        extends Activity
+        implements View.OnClickListener, Runnable {
+    protected final String LOG_TAG = "TestRunner";
+
+    private final Semaphore mSemaphore = new Semaphore(0);
+
+    private TextView mLogView;
+    private View mNextView;
+    private Thread mWorkerThread;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.snsr_semi_auto_test);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        mLogView = (TextView) this.findViewById(R.id.log_text);
+        mNextView = this.findViewById(R.id.next_button);
+        mNextView.setOnClickListener(this);
+        mLogView.setMovementMethod(new ScrollingMovementMethod());
+
+        updateButton(false /*enabled*/);
+        mWorkerThread = new Thread(this);
+        mWorkerThread.start();
+    }
+
+    @Override
+    public void onClick(View target) {
+        mSemaphore.release();
+    }
+
+    @Override
+    public void run() {
+        String message = "";
+        SensorTestResult testResult = SensorTestResult.PASS;
+        try {
+            onRun();
+        } catch(SensorNotSupportedException e) {
+            // the sensor is not supported/available in the device, log a warning and skip the test
+            testResult = SensorTestResult.SKIPPED;
+            message = e.getMessage();
+        } catch(Throwable e) {
+            testResult = SensorTestResult.FAIL;
+            message = e.getMessage();
+        }
+        setTestResult(testResult, message);
+        appendText("\nTest completed. Press 'Next' to finish.\n");
+        waitForUser();
+        finish();
+    }
+
+    /**
+     * This is the method that subclasses will implement to define the operations that need to be
+     * verified.
+     * Any exception thrown will cause the test to fail, additionally mAssert can be used to verify
+     * the tests state.
+     *
+     * throws Throwable
+     */
+    protected abstract void onRun() throws Throwable;
+
+    /**
+     * Helper methods for subclasses to interact with the UI and the operator.
+     */
+    protected void appendText(String text, int textColor) {
+        this.runOnUiThread(new TextAppender(mLogView, text, textColor));
+    }
+
+    protected void appendText(String text) {
+        this.runOnUiThread(new TextAppender(mLogView, text));
+    }
+
+    protected void updateButton(boolean enabled) {
+        this.runOnUiThread(new ButtonEnabler(this.mNextView, enabled));
+    }
+
+    protected void waitForUser() {
+        updateButton(true);
+        try {
+            mSemaphore.acquire();
+        } catch(InterruptedException e) {}
+        updateButton(false);
+    }
+
+    protected void logSuccess() {
+        appendText("PASS", Color.GREEN);
+    }
+
+    protected void playSound() {
+        MediaPlayer player = MediaPlayer.create(this, Settings.System.DEFAULT_NOTIFICATION_URI);
+        player.start();
+        try {
+            Thread.sleep(500);
+        } catch(InterruptedException e) {
+        } finally {
+            player.stop();
+        }
+    }
+
+    /**
+     * Private methods.
+     */
+    private String getTestId() {
+        return this.getClass().getName();
+    }
+
+    private void setTestResult(SensorTestResult testResult, String message) {
+        int textColor;
+        switch(testResult) {
+            case SKIPPED:
+                textColor = Color.YELLOW;
+                TestResult.setPassedResult(this, this.getTestId(), message);
+                break;
+            case PASS:
+                textColor = Color.GREEN;
+                TestResult.setPassedResult(this, this.getTestId(), message);
+                break;
+            case FAIL:
+                textColor = Color.RED;
+                TestResult.setFailedResult(this, this.getTestId(), message);
+                break;
+            default:
+                throw new InvalidParameterException("Unrecognized testResult.");
+        }
+        appendText(message, textColor);
+    }
+
+    private enum SensorTestResult {
+        SKIPPED,
+        PASS,
+        FAIL
+    }
+
+    private class TextAppender implements Runnable {
+        private final TextView mTextView;
+        private final SpannableStringBuilder mMessageBuilder;
+
+        public TextAppender(TextView textView, String message, int textColor) {
+            mTextView = textView;
+            mMessageBuilder = new SpannableStringBuilder(message + "\n");
+
+            ForegroundColorSpan colorSpan = new ForegroundColorSpan(textColor);
+            mMessageBuilder.setSpan(
+                    colorSpan,
+                    0 /*start*/,
+                    message.length(),
+                    Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+        }
+
+        public TextAppender(TextView textView, String message) {
+            this(textView, message, textView.getCurrentTextColor());
+        }
+
+        @Override
+        public void run() {
+            mTextView.append(mMessageBuilder);
+        }
+    }
+
+    private class ButtonEnabler implements Runnable {
+        private final View mButtonView;
+        private final boolean mButtonEnabled;
+
+        public ButtonEnabler(View buttonView, boolean buttonEnabled) {
+            mButtonView = buttonView;
+            mButtonEnabled = buttonEnabled;
+        }
+
+        @Override
+        public void run() {
+            mButtonView.setEnabled(mButtonEnabled);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
new file mode 100644
index 0000000..df15b66
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -0,0 +1,108 @@
+/*
+ * 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 com.android.cts.verifier.sensors;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.sensorTestOperations.VerifySignumOperation;
+
+/**
+ * Semi-automated test that focuses on characteristics associated with Accelerometer measurements.
+ */
+public class GyroscopeMeasurementTestActivity extends BaseSensorSemiAutomatedTestActivity {
+    @Override
+    protected void onRun() throws Throwable {
+        appendText("Place the device in a flat surface with the screen facing the ceiling, make "
+                + "sure the device aligns with the orientation specified for each scenario. Then "
+                + "follow the instructions for each scenario:");
+
+        verifyMeasurements(
+                "leave the device static",
+                true /*portrait*/,
+                0, 0, 0);
+
+        verifyMeasurements(
+                "rotate the device clockwise",
+                true /*portrait*/,
+                0, 0, -1);
+
+        verifyMeasurements(
+                "rotate the device counter-clockwise",
+                true /*portrait*/,
+                0, 0, +1);
+
+        verifyMeasurements(
+                "rotate the device on its right until it stands on its side",
+                true /*portrait*/,
+                0, +1, 0);
+
+        verifyMeasurements(
+                "rotate the device on its left until it stands on its side",
+                true /*portrait*/,
+                0, -1, 0);
+
+        verifyMeasurements(
+                "rotate the device on its top until it stands perpendicular",
+                false /*portrait*/,
+                -1, 0, 0);
+
+        verifyMeasurements(
+                "rotate the device on its bottom until it stands perpendicular",
+                false /*portrait*/,
+                +1, 0, 0);
+    }
+
+    /**
+     * This test verifies that the Gyroscope measures angular speeds with the right direction.
+     * The test does not measure the range or scale, apart from filtering small readings that
+     * deviate from zero.
+     *
+     * The test takes a set of samples from the sensor under test and calculates the mean of each
+     * axis that the sensor data collects. It then compares it against the test expectations that
+     * are represented by signed values. It verifies that the readings have the right direction.
+
+     * The reference values are coupled to the orientation of the device. The test is susceptible to
+     * errors when the device is not oriented properly, the device has moved to slowly, or it has
+     * moved in more than the direction conducted.
+     *
+     * The error message 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 mean of values sampled from the sensor
+     */
+    private void verifyMeasurements(
+            String scenarioInstructions,
+            boolean usePortraitOrientation,
+            double ... 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(
+                getApplicationContext(),
+                Sensor.TYPE_GYROSCOPE,
+                SensorManager.SENSOR_DELAY_FASTEST,
+                expectations,
+                0.2 /*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
new file mode 100644
index 0000000..e3d2f68
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.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 com.android.cts.verifier.sensors;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.sensorTestOperations.VerifyNormOperation;
+import android.hardware.cts.helpers.sensorTestOperations.VerifyStandardDeviationOperation;
+
+/**
+ * Semi-automated test that focuses characteristics associated with Accelerometer measurements.
+ * These test cases require calibration of the sensor before performing the verifications.
+ * Also, it is recommended to execute these tests outdoors, or at least far from magnetic
+ * disturbances.
+ */
+public class MagneticFieldMeasurementTestActivity extends BaseSensorSemiAutomatedTestActivity {
+    @Override
+    protected void onRun() throws Throwable {
+        appendText("Please calibrate the Magnetometer by moving it in 8 shapes in different " +
+                "orientations.");
+        appendText("Then leave the device in a flat surface and press Next...\n");
+        waitForUser();
+
+        appendText("Verifying the Norm...");
+        verifyNorm();
+
+        appendText("\nVerifying the Standard Deviation...");
+        verifyStandardDeviation();
+    }
+
+    /**
+     * 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
+     */
+    private void verifyNorm() throws Throwable {
+        float expectedMagneticFieldEarth =
+                (SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
+        float magneticFieldEarthThreshold =
+                expectedMagneticFieldEarth - SensorManager.MAGNETIC_FIELD_EARTH_MIN;
+        VerifyNormOperation verifyNorm = new VerifyNormOperation(
+                this.getApplicationContext(),
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorManager.SENSOR_DELAY_FASTEST,
+                expectedMagneticFieldEarth,
+                magneticFieldEarthThreshold);
+        verifyNorm.execute();
+        logSuccess();
+    }
+
+    /**
+     * 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.
+     */
+    private void verifyStandardDeviation() throws Throwable {
+        VerifyStandardDeviationOperation verifyStdDev = new VerifyStandardDeviationOperation(
+                this.getApplicationContext(),
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorManager.SENSOR_DELAY_FASTEST,
+                0 /*reportLatencyInUs*/,
+                2f /* uT */);
+        verifyStdDev.execute();
+        logSuccess();
+    }
+}
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 5acb29c..e0ad6e5 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -14,6 +14,20 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+# Reusable Sensor test classes and helpers
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := cts-sensors-tests
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src/android/hardware/cts/helpers)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+
+# CtsHardwareTestCases package
+
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java
index 55c2637..116ac80 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java
@@ -101,7 +101,7 @@
      */
     public void testEventOrdering() throws Throwable {
         VerifyEventOrderingOperation operation = new VerifyEventOrderingOperation(
-                this,
+                this.getContext(),
                 mSensorType,
                 mSamplingRateInUs,
                 mReportLatencyInUs);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java
index c829c36..b35f515 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java
@@ -97,7 +97,7 @@
      */
     public void testMaxFrequency() throws Throwable {
         VerifyMaximumFrequencyOperation operation = new VerifyMaximumFrequencyOperation(
-                this,
+                this.getContext(),
                 mSensorType,
                 mReportLatencyInUs,
                 mThresholdPercentageOfNs);
@@ -147,7 +147,7 @@
      */
     public void testJittering() throws Throwable {
         VerifyJitteringOperation operation = new VerifyJitteringOperation(
-                this,
+                this.getContext(),
                 mSensorType,
                 mReportLatencyInUs,
                 mThresholdPercentageOfNs);
@@ -195,7 +195,7 @@
      * - the observed maximum sampling rate
      */
     public void testMaxFrequencyExpected() {
-        Sensor sensor = SensorCtsHelper.getSensor(this, mSensorType);
+        Sensor sensor = SensorCtsHelper.getSensor(this.getContext(), mSensorType);
         int samplingRateInUs = sensor.getMinDelay();
         String message = String.format(
                 "samplingRateInUs| expected:%d, actual:%d",
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index f96c885..a1aa760 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -18,6 +18,8 @@
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
+import android.content.Context;
+
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 
@@ -86,28 +88,28 @@
     public void testSensorsWithSeveralClients() throws Throwable {
         final int ITERATIONS = 50;
         final int BATCHING_RATE_IN_SECONDS = 5;
+        final Context context = this.getContext();
 
         int sensorTypes[] = {
                 Sensor.TYPE_ACCELEROMETER,
                 Sensor.TYPE_MAGNETIC_FIELD,
                 Sensor.TYPE_GYROSCOPE };
 
-        ParallelCompositeSensorTestOperation operation =
-                new ParallelCompositeSensorTestOperation(this);
+        ParallelCompositeSensorTestOperation operation = new ParallelCompositeSensorTestOperation();
         for(int sensorType : sensorTypes) {
             SensorTestOperation continuousOperation = new VerifyEventOrderingOperation(
-                    this,
+                    context,
                     sensorType,
                     SensorManager.SENSOR_DELAY_NORMAL,
                     0 /* reportLatencyInUs */);
-            operation.add(new RepeatingSensorTestOperation(this, continuousOperation, ITERATIONS));
+            operation.add(new RepeatingSensorTestOperation(continuousOperation, ITERATIONS));
 
             SensorTestOperation batchingOperation = new VerifyEventOrderingOperation(
-                    this,
+                    context,
                     sensorType,
-                    SensorTestInformation.getMaxSamplingRateInUs(this, sensorType),
+                    SensorTestInformation.getMaxSamplingRateInUs(context, sensorType),
                     SensorCtsHelper.getSecondsAsMicroSeconds(BATCHING_RATE_IN_SECONDS));
-            operation.add(new RepeatingSensorTestOperation(this, batchingOperation, ITERATIONS));
+            operation.add(new RepeatingSensorTestOperation(batchingOperation, ITERATIONS));
         }
         operation.execute();
     }
@@ -138,8 +140,7 @@
         final int INSTANCES_TO_USE = 5;
         final int ITERATIONS_TO_EXECUTE = 100;
 
-        ParallelCompositeSensorTestOperation operation =
-                new ParallelCompositeSensorTestOperation(this);
+        ParallelCompositeSensorTestOperation operation = new ParallelCompositeSensorTestOperation();
         int sensorTypes[] = {
                 Sensor.TYPE_ACCELEROMETER,
                 Sensor.TYPE_MAGNETIC_FIELD,
@@ -148,10 +149,10 @@
         for(int sensorType : sensorTypes) {
             for(int instance = 0; instance < INSTANCES_TO_USE; ++instance) {
                 SequentialCompositeSensorTestOperation sequentialOperation =
-                        new SequentialCompositeSensorTestOperation(this);
+                        new SequentialCompositeSensorTestOperation();
                 for(int iteration = 0; iteration < ITERATIONS_TO_EXECUTE; ++iteration) {
                     VerifyEventOrderingOperation sensorOperation = new VerifyEventOrderingOperation(
-                            this,
+                            this.getContext(),
                             sensorType,
                             this.generateSamplingRateInUs(sensorType),
                             this.generateReportLatencyInUs());
@@ -209,15 +210,17 @@
      * of several clients can lead to the failing state.
      */
     public void testSensorStoppingInteraction() throws Throwable {
+        Context context = this.getContext();
+
         SensorTestOperation tester = new VerifyEventOrderingOperation(
-                this,
+                context,
                 mSensorTypeTester,
                 SensorManager.SENSOR_DELAY_NORMAL,
                 0 /*reportLatencyInUs*/);
         tester.start();
 
         SensorTestOperation testee = new VerifyEventOrderingOperation(
-                this,
+                context,
                 mSensorTypeTestee,
                 SensorManager.SENSOR_DELAY_UI,
                 0 /*reportLatencyInUs*/);
@@ -252,7 +255,7 @@
             case 4:
             default:
                 int maxSamplingRate = SensorTestInformation.getMaxSamplingRateInUs(
-                        this,
+                        this.getContext(),
                         sensorType);
                 rate = maxSamplingRate * mGenerator.nextInt(10);
         }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java
index 7251fba..ea025ba 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java
@@ -105,7 +105,7 @@
      */
     public void testEventNorm() throws Throwable {
         VerifyNormOperation operation = new VerifyNormOperation(
-                this,
+                this.getContext(),
                 mSensorType,
                 mSamplingRateInUs,
                 mReferenceValue,
@@ -162,7 +162,7 @@
      */
     public void testStandardDeviation() throws Throwable {
         VerifyStandardDeviationOperation operation = new VerifyStandardDeviationOperation(
-                this,
+                this.getContext(),
                 mSensorType,
                 mSamplingRateInUs,
                 mReportLatencyInUs,
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 ce859d2..c8733ba 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -22,8 +22,6 @@
 
 import android.os.Environment;
 
-import android.test.AndroidTestCase;
-
 import android.util.Log;
 
 import java.io.DataOutputStream;
@@ -63,7 +61,20 @@
         return arrayCopy.get(arrayIndex);
     }
 
-    // TODO: are there any internal libraries for this?
+    /**
+     * Calculates the mean for each of the values in the set of TestSensorEvents.
+     */
+    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);
 
@@ -186,10 +197,12 @@
         return outputFile;
     }
 
-    public static Sensor getSensor(AndroidTestCase testCase, int sensorType) {
-        SensorManager sensorManager = (SensorManager)testCase.getContext().getSystemService(
+    public static Sensor getSensor(Context context, int sensorType) {
+        SensorManager sensorManager = (SensorManager)context.getSystemService(
                 Context.SENSOR_SERVICE);
-        testCase.assertNotNull(sensorManager);
+        if(sensorManager == null) {
+            throw new IllegalStateException("SensorService is not present in the system.");
+        }
         Sensor sensor = sensorManager.getDefaultSensor(sensorType);
         if(sensor == null) {
             throw new SensorNotSupportedException(sensorType);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java
index dd29cb1..b46b06d 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java
@@ -24,7 +24,6 @@
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
-import android.test.AndroidTestCase;
 
 import java.io.Closeable;
 
@@ -44,7 +43,6 @@
 public class SensorManagerTestVerifier implements Closeable {
     private final int WAIT_TIMEOUT_IN_SECONDS = 30;
 
-    private final Assert mAssert;
     private final SensorManager mSensorManager;
     private final Sensor mSensorUnderTest;
     private final int mSamplingRateInUs;
@@ -56,15 +54,12 @@
      * Construction methods.
      */
     public SensorManagerTestVerifier(
-            AndroidTestCase testCase,
+            Context context,
             int sensorType,
             int samplingRateInUs,
             int reportLatencyInUs) {
-        mAssert = testCase;
-
-        mSensorManager = (SensorManager)testCase.getContext().getSystemService(
-                Context.SENSOR_SERVICE);
-        mSensorUnderTest = SensorCtsHelper.getSensor(testCase, sensorType);
+        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+        mSensorUnderTest = SensorCtsHelper.getSensor(context, sensorType);
         mSamplingRateInUs = samplingRateInUs;
         mReportLatencyInUs = reportLatencyInUs;
 
@@ -93,7 +88,7 @@
                 "registerListener",
                 mSensorUnderTest,
                 debugInfo);
-        mAssert.assertTrue(message, result);
+        Assert.assertTrue(message, result);
     }
 
     public void registerListener() {
@@ -116,6 +111,10 @@
         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);
@@ -133,7 +132,7 @@
                 "Flush",
                 mSensorUnderTest,
                 "" /* format */);
-        mAssert.assertTrue(message, mSensorManager.flush(mEventListener));
+        Assert.assertTrue(message, mSensorManager.flush(mEventListener));
     }
 
     public void waitForFlush() throws InterruptedException {
@@ -163,11 +162,10 @@
 
         @Override
         public void onSensorChanged(SensorEvent event) {
-            CountDownLatch latch = mEventLatch;
-            if(latch != null) {
-                // copy the event because there is no better way to do this in the platform
-                mSensorEventsList.addLast(new TestSensorEvent(event));
-                latch.countDown();
+            // 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();
             }
         }
 
@@ -192,7 +190,7 @@
                         "WaitForFlush",
                         mSensorUnderTest,
                         "" /* format */);
-                mAssert.assertTrue(message, latch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
+                Assert.assertTrue(message, latch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
             }
         }
 
@@ -202,6 +200,7 @@
 
         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?
@@ -213,7 +212,7 @@
                         eventCount,
                         mSensorEventsList.size(),
                         timeoutInfo);
-                mAssert.assertTrue(message, awaitCompleted);
+                Assert.assertTrue(message, awaitCompleted);
             } catch(InterruptedException e) {
             } finally {
                 mEventLatch = null;
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 f7bdbb7..90e0706 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
@@ -16,9 +16,9 @@
 
 package android.hardware.cts.helpers;
 
-import android.hardware.Sensor;
+import android.content.Context;
 
-import android.test.AndroidTestCase;
+import android.hardware.Sensor;
 
 import java.security.InvalidParameterException;
 
@@ -46,10 +46,10 @@
 //                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_GRAVITY:
+                return 3;
+            case Sensor.TYPE_LINEAR_ACCELERATION:
+                return 3;
 //            case Sensor.TYPE_ROTATION_VECTOR:
 //                return "Rotation Vector";
 //            case Sensor.TYPE_RELATIVE_HUMIDITY:
@@ -72,7 +72,7 @@
 //                return "Geomagnetic Rotation Vector";
             default:
                 throw new InvalidParameterException(
-                        String.format("Invalid sensorType:%d", sensorType));
+                        String.format("Invalid sensorType:%d. Unable to find axis count.", sensorType));
         }
     }
 
@@ -145,8 +145,8 @@
         return String.format("%s (%d)", name, sensorType);
     }
 
-    public static int getMaxSamplingRateInUs(AndroidTestCase testCase, int sensorType) {
-        Sensor sensor = SensorCtsHelper.getSensor(testCase, sensorType);
+    public static int getMaxSamplingRateInUs(Context context, int sensorType) {
+        Sensor sensor = SensorCtsHelper.getSensor(context, sensorType);
         return sensor.getMinDelay();
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java
index e87b289..902c8024 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java
@@ -35,16 +35,10 @@
     protected final long WAIT_TIMEOUT_IN_MILLISECONDS =
             TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES);
 
-    protected final Assert mAssert;
-
     private Thread mThread;
 
     protected int mIterationCount;
 
-    protected SensorTestOperation(Assert assertionObject) {
-        mAssert = assertionObject;
-    }
-
     /**
      * Public API definition.
      */
@@ -84,7 +78,7 @@
                     operationName,
                     this.toString(),
                     SensorCtsHelper.collectBugreport(operationName));
-            mAssert.fail(message);
+            Assert.fail(message);
         }
         mThread = null;
         mExceptionHandler.rethrow();
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
index 11fb81f..3730f4b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java
@@ -16,8 +16,6 @@
 
 package android.hardware.cts.helpers.sensorTestOperations;
 
-import junit.framework.Assert;
-
 import android.hardware.cts.helpers.SensorTestOperation;
 
 import java.util.ArrayList;
@@ -30,10 +28,6 @@
 public class ParallelCompositeSensorTestOperation extends SensorTestOperation {
     private final ArrayList<SensorTestOperation> mOperations = new ArrayList<SensorTestOperation>();
 
-    public ParallelCompositeSensorTestOperation(Assert assertionObject) {
-        super(assertionObject);
-    }
-
     /**
      * There is no synchronization
      * @param operations
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
index 35782c1..7a3c450 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java
@@ -16,8 +16,6 @@
 
 package android.hardware.cts.helpers.sensorTestOperations;
 
-import junit.framework.Assert;
-
 import android.hardware.cts.helpers.SensorTestOperation;
 
 /**
@@ -27,11 +25,7 @@
     private final SensorTestOperation mSensorTestOperation;
     private final int mRepetitionCount;
 
-    public RepeatingSensorTestOperation(
-            Assert assertionObject,
-            SensorTestOperation operation,
-            int repetitionCount) {
-        super(assertionObject);
+    public RepeatingSensorTestOperation(SensorTestOperation operation, int repetitionCount) {
         mSensorTestOperation = operation;
         mRepetitionCount = repetitionCount;
     }
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
index cd879f5..4b921680 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java
@@ -16,8 +16,6 @@
 
 package android.hardware.cts.helpers.sensorTestOperations;
 
-import junit.framework.Assert;
-
 import android.hardware.cts.helpers.SensorTestOperation;
 
 import java.util.ArrayList;
@@ -30,10 +28,6 @@
 public class SequentialCompositeSensorTestOperation extends SensorTestOperation {
     private final ArrayList<SensorTestOperation> mOperations = new ArrayList<SensorTestOperation>();
 
-    public SequentialCompositeSensorTestOperation(Assert assertionObject) {
-        super(assertionObject);
-    }
-
     /**
      * There is no synchronization
      * @param operations
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
index 90824b2..bb43b01 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java
@@ -16,13 +16,15 @@
 
 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;
 
-import android.test.AndroidTestCase;
-
 /**
  * Test Operation class that validates the ordering of sensor events.
  */
@@ -30,13 +32,12 @@
     private SensorManagerTestVerifier mSensor;
 
     public VerifyEventOrderingOperation(
-            AndroidTestCase testCase,
+            Context context,
             int sensorType,
             int samplingRateInUs,
             int reportLatencyInUs) {
-        super(testCase);
         mSensor = new SensorManagerTestVerifier(
-                testCase,
+                context,
                 sensorType,
                 samplingRateInUs,
                 reportLatencyInUs);
@@ -48,17 +49,19 @@
         for(int i = 1; i < events.length; ++i) {
             long previousTimestamp = events[i-1].timestamp;
             long timestamp = events[i].timestamp;
-            String message = SensorCtsHelper.formatAssertionMessage(
-                    "Ordering",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    "position:%d, previous:%d, timestamp:%d",
-                    i,
-                    previousTimestamp,
-                    timestamp);
             // allow two identical timestamps to be considered in order, in case the resolution of
             // the timestamp is not granular enough
-            mAssert.assertTrue(message, previousTimestamp <= timestamp);
+            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
index 5aac8af..6f1c03a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java
@@ -16,18 +16,21 @@
 
 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.test.AndroidTestCase;
-
 import android.util.Log;
 
 import java.security.InvalidParameterException;
+
 import java.util.ArrayList;
+
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -44,18 +47,17 @@
     protected long mThresholdInNs;
 
     public VerifyJitteringOperation(
-            AndroidTestCase testCase,
+            Context context,
             int sensorType,
             int reportLatencyInUs,
             int thresholdPercentageOfNs) throws InvalidParameterException {
-        super(testCase);
         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(testCase, sensorType);
+        int maxSamplingRateInUs = SensorTestInformation.getMaxSamplingRateInUs(context, sensorType);
         mSensor = new SensorManagerTestVerifier(
-                testCase,
+                context,
                 sensorType,
                 maxSamplingRateInUs,
                 reportLatencyInUs);
@@ -88,7 +90,7 @@
                     mThresholdPercentage,
                     percentile95InNs,
                     actualPercentValue);
-            mAssert.fail(message);
+            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
index 94fef60..3cdffe0 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java
@@ -16,14 +16,16 @@
 
 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.test.AndroidTestCase;
-
 import android.util.Log;
 
 import java.security.InvalidParameterException;
@@ -45,18 +47,17 @@
     protected long mThresholdInNs;
 
     public VerifyMaximumFrequencyOperation(
-            AndroidTestCase testCase,
+            Context context,
             int sensorType,
             int reportLatencyInUs,
             int thresholdPercentageOfNs) throws InvalidParameterException {
-        super(testCase);
         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(testCase, sensorType);
+        int maxSamplingRateInUs = SensorTestInformation.getMaxSamplingRateInUs(context, sensorType);
         mSensor = new SensorManagerTestVerifier(
-                testCase,
+                context,
                 sensorType,
                 maxSamplingRateInUs,
                 reportLatencyInUs);
@@ -91,7 +92,7 @@
                     SensorCtsHelper.getFrequencyInHz(frequencyMeanInUs),
                     mThresholdInNs,
                     mThresholdPercentage);
-            mAssert.fail(message);
+            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
new file mode 100644
index 0000000..d0c991b3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMeasurementsOperation.java
@@ -0,0 +1,97 @@
+/*
+ * 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
index e3b64e7..cce3412 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyNormOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyNormOperation.java
@@ -1,5 +1,5 @@
 /*
- * Copyri The Android Open Source Project
+ * 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.
@@ -16,14 +16,16 @@
 
 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.test.AndroidTestCase;
-
 /**
  * Test Operation class that validates the norm of a given sensor.
  * The operation relies in the number of axes each sensor type reports.
@@ -35,14 +37,13 @@
     private double mThreshold;
 
     public VerifyNormOperation(
-            AndroidTestCase testCase,
+            Context context,
             int sensorType,
             int samplingRateInUs,
             float referenceValue,
             float threshold) {
-        super(testCase);
         mSensor = new SensorManagerTestVerifier(
-                testCase,
+                context,
                 sensorType,
                 samplingRateInUs,
                 0 /*reportLatencyInUs*/);
@@ -76,6 +77,6 @@
                 mThreshold,
                 norm,
                 valuesBuilder.toString());
-        mAssert.assertTrue(message, Math.abs(mReferenceValue - norm) <= mThreshold);
+        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
new file mode 100644
index 0000000..0ccf92a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifySignumOperation.java
@@ -0,0 +1,111 @@
+/*
+ * 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
index bc0a498..89cff94 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java
@@ -16,14 +16,16 @@
 
 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.test.AndroidTestCase;
-
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -37,14 +39,13 @@
     private double mExpectedStandardDeviation;
 
     public VerifyStandardDeviationOperation(
-            AndroidTestCase testCase,
+            Context context,
             int sensorType,
             int samplingRateInUs,
             int reportLatencyInUs,
             float expectedStandardDeviation) {
-        super(testCase);
         mSensor = new SensorManagerTestVerifier(
-                testCase,
+                context,
                 sensorType,
                 samplingRateInUs,
                 reportLatencyInUs);
@@ -75,7 +76,7 @@
                         i,
                         mExpectedStandardDeviation,
                         standardDeviation);
-                mAssert.fail(message);
+                Assert.fail(message);
             }
         }
     }