am 774c0157: Sensor batch test fixes.

* commit '774c01574557a7b544f5d41473d3448a8d88581a':
  Sensor batch test fixes.
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 3306151..e151325 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -760,6 +760,16 @@
             <meta-data android:name="test_category" android:value="@string/test_category_sensors"/>
         </activity>
 
+        <activity android:name=".sensors.SensorBatchingTestsActivity"
+                  android:label="@string/snsr_sensor_batching_tests"
+                  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"/>
+        </activity>
+
         <activity android:name=".sensors.SensorIntegrationTestsActivity"
                   android:label="@string/snsr_sensor_integration_tests">
             <intent-filter>
diff --git a/apps/CtsVerifier/res/layout/snsr_instruction.xml b/apps/CtsVerifier/res/layout/snsr_instruction.xml
index 66c7386..e7369c7 100644
--- a/apps/CtsVerifier/res/layout/snsr_instruction.xml
+++ b/apps/CtsVerifier/res/layout/snsr_instruction.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:textColor="?android:attr/textColorSecondary"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingBottom="@dimen/snsr_log_padding_bottom"
diff --git a/apps/CtsVerifier/res/layout/snsr_message.xml b/apps/CtsVerifier/res/layout/snsr_message.xml
new file mode 100644
index 0000000..cef35ca
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/snsr_message.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:textStyle="italic"
+        android:textColor="?android:attr/textColorTertiary"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="@dimen/snsr_log_padding_top"
+        android:paddingLeft="@dimen/snsr_boxed_padding_left"
+        android:paddingRight="@dimen/snsr_boxed_padding_right"
+        android:paddingBottom="@dimen/snsr_log_padding_bottom" />
diff --git a/apps/CtsVerifier/res/layout/snsr_test_title.xml b/apps/CtsVerifier/res/layout/snsr_test_title.xml
index 2a458e7..374fb54 100644
--- a/apps/CtsVerifier/res/layout/snsr_test_title.xml
+++ b/apps/CtsVerifier/res/layout/snsr_test_title.xml
@@ -14,10 +14,11 @@
      limitations under the License.
 -->
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:textStyle="bold"
+        android:textColor="?android:attr/textColorPrimary"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingBottom="@dimen/snsr_boxed_padding_bottom"
         android:paddingTop="@dimen/snsr_boxed_padding_top"
         android:paddingLeft="@dimen/snsr_boxed_padding_left"
-        android:paddingRight="@dimen/snsr_boxed_padding_right"
-        android:textStyle="bold" />
+        android:paddingRight="@dimen/snsr_boxed_padding_right" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index a3b920f..ea86111 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -432,7 +432,6 @@
     <string name="snsr_executing_test">\nExecuting test case \'%1$s\'..\n</string>
     <string name="snsr_orientation_portrait">[Device orientation]: Portrait.</string>
     <string name="snsr_orientation_landscape">[Device orientation]: Landscape.</string>
-    <string name="snsr_register_listener">Expected to be able to register sensor listener. Found=%b.</string>
 
     <!-- Strings to interact with users in Sensor Tests -->
     <string name="snsr_test_play_sound">A sound will be played once the verification is complete...</string>
@@ -517,8 +516,6 @@
     <string name="snsr_batch_test">Sensor Batching Tests</string>
     <string name="snsr_batching_walking_needed">Once the test begins, you will have to walk.</string>
     <string name="snsr_batching_fifo_count">FifoReservedEventCount=%1$d. Expected to be at most FifoMaxEventCount=%2$d.</string>
-    <string name="snsr_batching_first_event_arrival">Batched events expected to arrive after %1$d ns. Found=%2$d ns.</string>
-    <string name="snsr_batching_flush_complete">Event \'onFlushComplete\' expected. Found=%1$b.</string>
 
     <!-- Sensor Synchronization -->
     <string name="snsr_synch_test">Sensor Synchronization Test</string>
@@ -555,6 +552,7 @@
     <string name="snsr_single_sensor_tests">CTS Single Sensor Tests</string>
     <string name="snsr_sensor_integration_tests">CTS Sensor Integration Tests</string>
     <string name="snsr_sensor_test">CTS Sensor Test</string>
+    <string name="snsr_sensor_batching_tests">CTS Sensor Batching Tests</string>
 
     <!-- Strings for Sample Test Activities -->
     <string name="share_button_text">Share</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
index 009e644..42bd6ad 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -19,22 +19,15 @@
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
 
-import junit.framework.Assert;
-
 import android.content.Context;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.SensorNotSupportedException;
-import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
 import android.os.Bundle;
 import android.os.PowerManager;
-import android.os.SystemClock;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -44,27 +37,19 @@
  * If a sensor supports the batching mode, FifoReservedEventCount for that sensor should be greater
  * than one.
  */
-public class BatchingTestActivity
-        extends SensorCtsVerifierTestActivity
-        implements SensorEventListener2 {
+public class BatchingTestActivity extends SensorCtsVerifierTestActivity {
     public BatchingTestActivity() {
         super(BatchingTestActivity.class);
     }
 
-    private final long TWO_SECONDS_MILLIS = TimeUnit.SECONDS.toMillis(2);
-    private static final long DATA_COLLECTION_TIME_IN_MS = TimeUnit.SECONDS.toMillis(10);
-    private final long MIN_BATCH_TIME_NANOS = TimeUnit.SECONDS.toNanos(5);
-    private final long MAX_BATCH_REPORT_LATENCY_US = DATA_COLLECTION_TIME_IN_MS * 500;
+    private static final int SENSOR_BATCHING_RATE_US = SensorManager.SENSOR_DELAY_FASTEST;
+    private static final int REPORT_LATENCY_10_SEC = 10;
+    private static final int BATCHING_PADDING_TIME_S = 2;
 
-    private final List<TestSensorEvent> mSensorEvents = new ArrayList<TestSensorEvent>();
+    // we are testing sensors that only trigger based on external events, so leave enough time for
+    // such events to generate
+    private static final int REPORT_LATENCY_25_SEC = 25;
 
-    private SensorManager mSensorManager;
-
-    private volatile Sensor mSensorUnderTest;
-    private volatile long mTimeFirstBatchedEventReceivedNanos;
-
-    private CountDownLatch mSensorEventReceived;
-    private CountDownLatch mFlushCompleteReceived;
     private PowerManager.WakeLock mWakeLock;
 
     @Override
@@ -74,7 +59,6 @@
 
     @Override
     protected void activitySetUp() throws InterruptedException {
-        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
         PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "BatchingTests");
 
@@ -88,147 +72,106 @@
         restoreSensorFeatures();
     }
 
-    // TODO: refactor to discover all available sensors of each type and dinamically generate test
+    // TODO: refactor to discover all available sensors of each type and dynamically generate test
     // cases for all of them
-    public String testStepCounter() throws Throwable {
-        return runTest(Sensor.TYPE_STEP_COUNTER, R.string.snsr_batching_walking_needed);
+    public String testStepCounter_batching() throws Throwable {
+        return runBatchTest(
+                Sensor.TYPE_STEP_COUNTER,
+                REPORT_LATENCY_25_SEC,
+                R.string.snsr_batching_walking_needed);
     }
 
-    public String testStepDetector() throws Throwable {
-        return  runTest(Sensor.TYPE_STEP_DETECTOR, R.string.snsr_batching_walking_needed);
+    public String testStepCounter_flush() throws Throwable {
+        return runFlushTest(
+                Sensor.TYPE_STEP_COUNTER,
+                REPORT_LATENCY_25_SEC,
+                R.string.snsr_batching_walking_needed);
     }
 
-    public String testProximity() throws Throwable {
-        return runTest(Sensor.TYPE_PROXIMITY, R.string.snsr_interaction_needed);
+    public String testStepDetector_batching() throws Throwable {
+        return  runBatchTest(
+                Sensor.TYPE_STEP_DETECTOR,
+                REPORT_LATENCY_25_SEC,
+                R.string.snsr_batching_walking_needed);
     }
 
-    public String testLight() throws Throwable {
-        return runTest(Sensor.TYPE_LIGHT, R.string.snsr_interaction_needed);
+    public String testStepDetector_flush() throws Throwable {
+        return  runFlushTest(
+                Sensor.TYPE_STEP_DETECTOR,
+                REPORT_LATENCY_25_SEC,
+                R.string.snsr_batching_walking_needed);
     }
 
-    // TODO: move sensors that do not require interaction to CTS
-    public String testGameRotationVector() throws Throwable {
-        return runTest(Sensor.TYPE_GAME_ROTATION_VECTOR, R.string.snsr_no_interaction);
+    public String testProximity_batching() throws Throwable {
+        return runBatchTest(
+                Sensor.TYPE_PROXIMITY,
+                REPORT_LATENCY_10_SEC,
+                R.string.snsr_interaction_needed);
     }
 
-    public String testGeomagneticRotationVector() throws Throwable {
-        return runTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, R.string.snsr_no_interaction);
+    public String testProximity_flush() throws Throwable {
+        return runFlushTest(
+                Sensor.TYPE_PROXIMITY,
+                REPORT_LATENCY_10_SEC,
+                R.string.snsr_interaction_needed);
     }
 
-    public String testAccelerometer() throws Throwable {
-        return runTest(Sensor.TYPE_ACCELEROMETER, R.string.snsr_no_interaction);
+    public String testLight_batching() throws Throwable {
+        return runBatchTest(
+                Sensor.TYPE_LIGHT,
+                REPORT_LATENCY_10_SEC,
+                R.string.snsr_interaction_needed);
     }
 
-    public String testGyroscope() throws Throwable {
-        return runTest(Sensor.TYPE_GYROSCOPE, R.string.snsr_no_interaction);
+    public String testLight_flush() throws Throwable {
+        return runFlushTest(
+                Sensor.TYPE_LIGHT,
+                REPORT_LATENCY_10_SEC,
+                R.string.snsr_interaction_needed);
     }
 
-    public String testGyroscopeUncalibrated() throws Throwable {
-        return runTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, R.string.snsr_no_interaction);
+    private String runBatchTest(int sensorType, int maxBatchReportLatencySec, int instructionsResId)
+            throws Throwable {
+        getTestLogger().logInstructions(instructionsResId);
+        waitForUserToBegin();
+
+        Context context = getApplicationContext();
+        int maxBatchReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(maxBatchReportLatencySec);
+        int testDurationSec = maxBatchReportLatencySec + BATCHING_PADDING_TIME_S;
+        TestSensorOperation operation = new TestSensorOperation(
+                context,
+                sensorType,
+                SENSOR_BATCHING_RATE_US,
+                maxBatchReportLatencyUs,
+                testDurationSec,
+                TimeUnit.SECONDS);
+
+        return executeTest(operation);
     }
 
-    public String testMagneticField() throws Throwable {
-        return runTest(Sensor.TYPE_MAGNETIC_FIELD, R.string.snsr_no_interaction);
+    private String runFlushTest(int sensorType, int maxBatchReportLatencySec, int instructionsResId)
+            throws Throwable {
+        getTestLogger().logInstructions(instructionsResId);
+        waitForUserToBegin();
+
+        Context context = getApplicationContext();
+        int maxBatchReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(maxBatchReportLatencySec);
+        int flushDurationSec = maxBatchReportLatencySec / 2;
+        TestSensorFlushOperation operation = new TestSensorFlushOperation(
+                context,
+                sensorType,
+                SENSOR_BATCHING_RATE_US,
+                maxBatchReportLatencyUs,
+                flushDurationSec,
+                TimeUnit.SECONDS);
+
+        return executeTest(operation);
     }
 
-    public String testMagneticFieldUncalibrated() throws Throwable {
-        return runTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, R.string.snsr_no_interaction);
-    }
-
-    public String testRotationVector() throws Throwable {
-        return runTest(Sensor.TYPE_ROTATION_VECTOR, R.string.snsr_no_interaction);
-    }
-
-    // TODO: split batching and flush scenarios
-    private String runTest(int sensorType, int instructionsResId) throws Throwable {
-        mSensorUnderTest = mSensorManager.getDefaultSensor(sensorType);
-        // TODO: add exception for batching not supported
-        if (mSensorUnderTest == null || mSensorUnderTest.getFifoMaxEventCount() < 1) {
-            throw new SensorNotSupportedException(Sensor.TYPE_STEP_COUNTER);
-        }
-
-        appendText(instructionsResId);
-        waitForUser();
-
-        // Register to wait for first sensor event arrival, and when FIFO has been flushed
-        mSensorEventReceived = new CountDownLatch(1);
-        mFlushCompleteReceived = new CountDownLatch(1);
-        mSensorEvents.clear();
-
-        int fifoReservedEventCount = mSensorUnderTest.getFifoReservedEventCount();
-        int fifoMaxEventCount = mSensorUnderTest.getFifoMaxEventCount();
-        String fifoMessage = getString(
-                R.string.snsr_batching_fifo_count,
-                fifoReservedEventCount,
-                fifoMaxEventCount);
-        Assert.assertTrue(fifoMessage, fifoReservedEventCount <= fifoMaxEventCount);
-
-        // Time when start batching
-        mTimeFirstBatchedEventReceivedNanos = 0;
-        long timeBatchingStartedNanos = SystemClock.elapsedRealtimeNanos();
-
-        // Batch with the fastest rate and set report latency large enough to ensure full batching
-        // occurs
-        boolean registerResult = mSensorManager.registerListener(
-                this /* listener */,
-                mSensorUnderTest,
-                SensorManager.SENSOR_DELAY_FASTEST,
-                (int) MAX_BATCH_REPORT_LATENCY_US);
-        Assert.assertTrue(
-                getString(R.string.snsr_register_listener, registerResult),
-                registerResult);
-
-        // add a buffer to the duration of the test for timeout
-        mSensorEventReceived
-                .await(DATA_COLLECTION_TIME_IN_MS + TWO_SECONDS_MILLIS, TimeUnit.MILLISECONDS);
-        // TODO: add delayed assertion for await
-
-        // verify the minimum batching time
-        long firstTimeArrivalDelta = mTimeFirstBatchedEventReceivedNanos - timeBatchingStartedNanos;
-        String firstTimeArrivalMessage = getString(
-                R.string.snsr_batching_first_event_arrival,
-                MIN_BATCH_TIME_NANOS,
-                firstTimeArrivalDelta);
-        Assert.assertTrue(firstTimeArrivalMessage, firstTimeArrivalDelta >= MIN_BATCH_TIME_NANOS);
-
-        // batch a bit more to test the flush
-        long sleepTime = TimeUnit.NANOSECONDS.toMillis(MIN_BATCH_TIME_NANOS / 2);
-        Thread.sleep(sleepTime);
-        mSensorManager.flush(this);
-
-        boolean flushAwaitSuccess = mFlushCompleteReceived
-                .await(DATA_COLLECTION_TIME_IN_MS + TWO_SECONDS_MILLIS, TimeUnit.MILLISECONDS);
-        Assert.assertTrue(
-                getString(R.string.snsr_batching_flush_complete, flushAwaitSuccess),
-                flushAwaitSuccess);
-
-        playSound();
-        // TODO: use SensorTestVerifications to check for event ordering and event gap
+    private String executeTest(VerifiableSensorOperation operation) {
+        operation.addDefaultVerifications();
+        operation.setLogEvents(true);
+        operation.execute();
         return null;
     }
-
-    @Override
-    public void onSensorChanged(SensorEvent sensorEvent) {
-        long elapsedTime = SystemClock.elapsedRealtimeNanos();
-        if (sensorEvent.sensor.getType() != mSensorUnderTest.getType()) {
-            // TODO: add delayed assertion
-            return;
-        }
-
-        mSensorEvents.add(new TestSensorEvent(sensorEvent, elapsedTime));
-        if (mTimeFirstBatchedEventReceivedNanos == 0) {
-            mTimeFirstBatchedEventReceivedNanos = elapsedTime;
-            mSensorEventReceived.countDown();
-        }
-    }
-
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
-    }
-
-    @Override
-    public void onFlushCompleted(Sensor sensor) {
-        mSensorManager.unregisterListener(this);
-        mFlushCompleteReceived.countDown();
-    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorBatchingTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorBatchingTestsActivity.java
new file mode 100644
index 0000000..d1923cc
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorBatchingTestsActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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 com.android.cts.verifier.sensors;
+
+import com.android.cts.verifier.sensors.base.SensorCtsTestActivity;
+
+import android.hardware.cts.SensorBatchingTests;
+
+/**
+ * Activity to execute CTS sensor batching tests.
+ * It is a wrapper for {@link SensorBatchingTests} running with AndroidJUnitRunner.
+ */
+public class SensorBatchingTestsActivity extends SensorCtsTestActivity {
+    public SensorBatchingTestsActivity() {
+        super(SensorBatchingTests.class);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
index 78430e3..221c92a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
@@ -29,8 +29,9 @@
 import android.hardware.cts.helpers.MovementDetectorHelper;
 import android.hardware.cts.helpers.TestSensorEvent;
 import android.os.SystemClock;
+import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
+import android.widget.ScrollView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -66,6 +67,10 @@
     private final List<TestSensorEvent> mStepCounterEvents = new ArrayList<TestSensorEvent>();
     private final List<TestSensorEvent> mStepDetectorEvents = new ArrayList<TestSensorEvent>();
 
+    /**
+     * A flag that indicates if the test is interested in registering steps reported by the
+     * operator. The registration of events happens by tapping the screen throughout the test.
+     */
     private volatile boolean mCheckForMotion;
 
     @Override
@@ -74,16 +79,19 @@
         mSensorStepCounter = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
         mSensorStepDetector = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
 
-        View screen = findViewById(R.id.log_layout);
-        screen.setOnClickListener(new OnClickListener() {
+        ScrollView scrollView = (ScrollView) findViewById(R.id.log_scroll_view);
+        scrollView.setOnTouchListener(new View.OnTouchListener() {
             @Override
-            public void onClick(View v) {
+            public boolean onTouch(View v, MotionEvent event) {
+                // during movement of the device, the ScrollView will detect user taps as attempts
+                // to scroll, when in reality they are taps in the layout
+                // to overcome the fact that a ScrollView cannot be disabled from scrolling, we
+                // listen for ACTION_UP events instead of click events in the child layout
                 long elapsedTime = SystemClock.elapsedRealtimeNanos();
-                if (mCheckForMotion) {
-                    playSound();
-                    mTimestampsUserReported.add(SystemClock.elapsedRealtimeNanos());
-                    appendText(getString(R.string.snsr_step_reported, elapsedTime));
+                if (event.getAction() == MotionEvent.ACTION_UP) {
+                    logUserReportedStep(elapsedTime);
                 }
+                return false;
             }
         });
     }
@@ -123,8 +131,8 @@
         mMoveDetected = false;
         mCheckForMotion = false;
 
-        appendText(instructionsResId);
-        waitForUser();
+        getTestLogger().logMessage(instructionsResId);
+        waitForUserToBegin();
 
         mCheckForMotion = (expectedSteps > 0);
         if (vibrate) {
@@ -299,16 +307,25 @@
         int type = event.sensor.getType();
         if (type == Sensor.TYPE_STEP_COUNTER) {
             mStepCounterEvents.add(new TestSensorEvent(event, elapsedRealtimeNanos));
-            String counterMessage = getString(
+            getTestLogger().logMessage(
                     R.string.snsr_step_counter_event,
                     elapsedRealtimeNanos,
                     (int) event.values[0]);
-            appendText(counterMessage);
         } else if (type == Sensor.TYPE_STEP_DETECTOR) {
             mStepDetectorEvents.add(new TestSensorEvent(event, elapsedRealtimeNanos));
-            appendText(getString(R.string.snsr_step_detector_event, elapsedRealtimeNanos));
+            getTestLogger().logMessage(R.string.snsr_step_detector_event, elapsedRealtimeNanos);
 
         }
         // TODO: with delayed assertions check events of other types are tracked
     }
+
+    private void logUserReportedStep(long timestamp) {
+        if (!mCheckForMotion) {
+            return;
+        }
+
+        playSound();
+        mTimestampsUserReported.add(timestamp);
+        getTestLogger().logMessage(R.string.snsr_step_reported, timestamp);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
index 2938dff..04603cb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
@@ -402,6 +402,12 @@
             textAppender.append();
         }
 
+        public void logMessage(int messageResId, Object ... params) {
+            TextAppender textAppender = new TextAppender(R.layout.snsr_message);
+            textAppender.setText(getString(messageResId, params));
+            textAppender.append();
+        }
+
         public void logTestDetails(SensorTestDetails testDetails) {
             String name = testDetails.getName();
             String summary = testDetails.getSummary();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java
index 9a9b1cb..6ca4e30 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java
@@ -64,7 +64,7 @@
     public SensorTestDetails(Context context, String name, Result result) {
         this(context,
                 name,
-                result.getRunCount(),
+                result.getRunCount() - result.getFailureCount() - result.getIgnoreCount(),
                 result.getIgnoreCount(),
                 result.getFailureCount());
     }
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 5b32d40..153445d 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -32,6 +32,7 @@
     src/android/hardware/cts/SensorTestCase.java \
     src/android/hardware/cts/SingleSensorTests.java \
     src/android/hardware/cts/SensorIntegrationTests.java \
+    src/android/hardware/cts/SensorBatchingTests.java \
     src/android/hardware/cts/SensorTest.java \
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
new file mode 100644
index 0000000..3d66b50
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
@@ -0,0 +1,319 @@
+/*
+ * 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.content.Context;
+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.TestSensorFlushOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Set of tests to verify that sensors operate correctly when operating in batching mode.
+ * This class defines tests for continuous sensors when the device is awake.
+ * On-change and special sensors are tested separately inside CtsVerifier, and they are defined in:
+ * {@link com.android.cts.verifier.sensors.BatchingTestActivity}.
+ *
+ * Each test is expected to pass even if batching is not supported for a particular sensor. This is
+ * usually achieved by ensuring that {@link ISensorVerification}s fallback accordingly.
+ *
+ * <p>To execute these test cases, the following command can be used:</p>
+ * <pre>
+ * adb shell am instrument -e class android.hardware.cts.SensorBatchingTests \
+ *     -w com.android.cts.hardware/android.test.AndroidJUnitRunner
+ * </pre>
+ */
+public class SensorBatchingTests extends SensorTestCase {
+    private static final String TAG = "SensorBatchingTests";
+
+    private static final int BATCHING_10S = 10;
+    private static final int RATE_50HZ = 20000;
+    private static final int RATE_FASTEST = SensorManager.SENSOR_DELAY_FASTEST;
+
+    /**
+     * An arbitrary 'padding' time slot to wait for events after batching latency expires.
+     * This allows for the test to wait for event arrivals after batching was expected.
+     */
+    private static final int BATCHING_PADDING_TIME_S = 2;
+
+    public void testAccelerometer_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testAccelerometer_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testAccelerometer_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testAccelerometer_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testMagneticField_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testMagneticField_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testMagneticField_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testMagneticField_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_10S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_ORIENTATION, RATE_FASTEST, BATCHING_10S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_10S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_ORIENTATION, RATE_FASTEST, BATCHING_10S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGyroscope_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GYROSCOPE, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGyroscope_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGyroscope_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_10S);
+    }
+
+    public void testGyroscope_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testPressure_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_PRESSURE, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testPressure_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testPressure_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_10S);
+    }
+
+    public void testPressure_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGravity_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GRAVITY, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGravity_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGravity_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_10S);
+    }
+
+    public void testGravity_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testRotationVector_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testRotationVector_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testRotationVector_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testRotationVector_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testMagneticFieldUncalibrated_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testMagneticFieldUncalibrated_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testMagneticFieldUncalibrated_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testMagneticFieldUncalibrated_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGameRotationVector_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGameRotationVector_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGameRotationVector_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGameRotationVector_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGyroscopeUncalibrated_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGyroscopeUncalibrated_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGyroscopeUncalibrated_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGyroscopeUncalibrated_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testLinearAcceleration_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testLinearAcceleration_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testLinearAcceleration_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testLinearAcceleration_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGeomagneticRotationVector_fastest_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGeomagneticRotationVector_50hz_batching() throws Throwable {
+        runBatchingSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+    }
+
+    public void testGeomagneticRotationVector_fastest_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+    }
+
+    public void testGeomagneticRotationVector_50hz_flush() throws Throwable {
+        runFlushSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+    }
+
+    private void runBatchingSensorTest(int sensorType, int rateUs, int maxBatchReportLatencySec)
+            throws Throwable {
+        Context context = getContext();
+        int maxBatchReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(maxBatchReportLatencySec);
+        int testDurationSec = maxBatchReportLatencySec + BATCHING_PADDING_TIME_S;
+        TestSensorOperation operation = new TestSensorOperation(
+                context,
+                sensorType,
+                rateUs,
+                maxBatchReportLatencyUs,
+                testDurationSec,
+                TimeUnit.SECONDS);
+
+        boolean flushExpected = false;
+        executeTest(operation, sensorType, rateUs, maxBatchReportLatencyUs, flushExpected);
+    }
+
+    private void runFlushSensorTest(int sensorType, int rateUs, int maxBatchReportLatencySec)
+            throws Throwable {
+        Context context = getContext();
+        int maxBatchReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(maxBatchReportLatencySec);
+        int flushDurationSec = maxBatchReportLatencySec / 2;
+        TestSensorFlushOperation operation = new TestSensorFlushOperation(
+                context,
+                sensorType,
+                rateUs,
+                maxBatchReportLatencyUs,
+                flushDurationSec,
+                TimeUnit.SECONDS);
+
+        boolean flushExpected = true;
+        executeTest(operation, sensorType, rateUs, maxBatchReportLatencyUs, flushExpected);
+    }
+
+    private void executeTest(
+            VerifiableSensorOperation operation,
+            int sensorType,
+            int rateUs,
+            int maxBatchReportLatencyUs,
+            boolean flushExpected) throws Throwable {
+        operation.addDefaultVerifications();
+        operation.setLogEvents(true);
+
+        try {
+            operation.execute();
+        } finally {
+            SensorStats.logStats(TAG, operation.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 flush = flushExpected ? "_flush" : "";
+            String fileName = String.format("sensor_batching_%s_%s%s%s.txt",
+                    sensorName, sensorRate, batching, flush);
+            SensorStats.logStatsToFile(fileName, operation.getStats());
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index 1b923fc..c471b7a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -15,6 +15,9 @@
  */
 package android.hardware.cts;
 
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
@@ -24,11 +27,9 @@
 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.sensoroperations.VerifiableSensorOperation;
 import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
 import java.util.Random;
 
 /**
@@ -225,7 +226,7 @@
                 100 /* event count */);
         tester.addVerification(new EventOrderingVerification());
 
-        TestSensorOperation testee = new TestSensorOperation(
+        VerifiableSensorOperation testee = new TestSensorOperation(
                 context,
                 mSensorTypeTestee,
                 SensorManager.SENSOR_DELAY_FASTEST,
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 5607123..a4d4157 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -518,8 +518,11 @@
         assertTrue(sensor.getResolution() >= 0);
         assertNotNull(sensor.getVendor());
         assertTrue(sensor.getVersion() > 0);
-        assertTrue(sensor.getFifoMaxEventCount() >= 0);
-        assertTrue(sensor.getFifoReservedEventCount() >= 0);
+        int fifoMaxEventCount = sensor.getFifoMaxEventCount();
+        int fifoReservedEventCount = sensor.getFifoReservedEventCount();
+        assertTrue(fifoMaxEventCount >= 0);
+        assertTrue(fifoReservedEventCount >= 0);
+        assertTrue(fifoReservedEventCount <= fifoMaxEventCount);
         if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
             assertTrue("One shot sensors should have zero FIFO Size",
                     sensor.getFifoMaxEventCount() == 0);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index cc7f71c..866e556 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -34,7 +34,7 @@
  * 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
+ *     -w com.android.cts.hardware/android.test.AndroidJUnitRunner
  * </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
@@ -86,9 +86,6 @@
 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_200HZ = 5000;
     private static final int RATE_100HZ = 10000;
     private static final int RATE_50HZ = 20000;
@@ -98,17 +95,8 @@
     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
+     * This test verifies that the sensor's properties complies with the required properties set in
      * the CDD.
      * <p>
      * It checks that the sampling rate advertised by the sensor under test matches that which is
@@ -138,513 +126,414 @@
     // 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);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testAccelerometer_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_100HZ);
     }
 
     public void testAccelerometer_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_200HZ);
     }
 
     public void testAccelerometer_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ);
     }
 
     public void testAccelerometer_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_25HZ);
     }
 
     public void testAccelerometer_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_15HZ);
     }
 
     public void testAccelerometer_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_10HZ);
     }
 
     public void testAccelerometer_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_1HZ);
     }
 
     public void testMagneticField_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testMagneticField_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_200HZ);
     }
 
     public void testMagneticField_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_100HZ);
     }
 
     public void testMagneticField_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ);
     }
 
     public void testMagneticField_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_25HZ);
     }
 
     public void testMagneticField_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_15HZ);
     }
 
     public void testMagneticField_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_10HZ);
     }
 
     public void testMagneticField_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_1HZ);
     }
 
     @SuppressWarnings("deprecation")
     public void testOrientation_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     @SuppressWarnings("deprecation")
     public void testOrientation_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_200HZ);
     }
     @SuppressWarnings("deprecation")
     public void testOrientation_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_100HZ);
     }
 
     @SuppressWarnings("deprecation")
     public void testOrientation_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ);
     }
 
     @SuppressWarnings("deprecation")
     public void testOrientation_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_25HZ);
     }
 
     @SuppressWarnings("deprecation")
     public void testOrientation_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_15HZ);
     }
 
     @SuppressWarnings("deprecation")
     public void testOrientation_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_10HZ);
     }
 
     @SuppressWarnings("deprecation")
     public void testOrientation_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_5HZ);
     }
 
     @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);
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_1HZ);
     }
 
     public void testGyroscope_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testGyroscope_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_200HZ);
     }
 
     public void testGyroscope_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_100HZ);
     }
 
     public void testGyroscope_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ);
     }
 
     public void testGyroscope_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_25HZ);
     }
 
     public void testGyroscope_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_15HZ);
     }
 
     public void testGyroscope_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_10HZ);
     }
 
     public void testGyroscope_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_1HZ);
     }
 
     public void testPressure_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testPressure_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_200HZ);
     }
 
     public void testPressure_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_100HZ);
     }
 
     public void testPressure_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ);
     }
 
     public void testPressure_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_25HZ);
     }
 
     public void testPressure_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_15HZ);
     }
 
     public void testPressure_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_10HZ);
     }
 
     public void testPressure_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_PRESSURE, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_1HZ);
     }
 
     public void testGravity_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testGravity_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_200HZ);
     }
 
     public void testGravity_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_100HZ);
     }
 
     public void testGravity_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ);
     }
 
     public void testGravity_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_25HZ);
     }
 
     public void testGravity_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_15HZ);
     }
 
     public void testGravity_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_10HZ);
     }
 
     public void testGravity_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GRAVITY, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_1HZ);
     }
 
     public void testRotationVector_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
-                BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testRotationVector_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_200HZ);
     }
 
     public void testRotationVector_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_100HZ);
     }
 
     public void testRotationVector_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ);
     }
 
     public void testRotationVector_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_25HZ);
     }
 
     public void testRotationVector_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_15HZ);
     }
 
     public void testRotationVector_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_10HZ);
     }
 
     public void testRotationVector_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_1HZ);
     }
 
     public void testMagneticFieldUncalibrated_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
-                BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testMagneticFieldUncalibrated_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_200HZ);
     }
 
     public void testMagneticFieldUncalibrated_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_100HZ);
     }
 
     public void testMagneticFieldUncalibrated_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ);
     }
 
     public void testMagneticFieldUncalibrated_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_25HZ);
     }
 
     public void testMagneticFieldUncalibrated_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_15HZ);
     }
 
     public void testMagneticFieldUncalibrated_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_10HZ);
     }
 
     public void testMagneticFieldUncalibrated_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_1HZ);
     }
 
     public void testGameRotationVector_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
-                BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testGameRotationVector_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_200HZ);
     }
 
     public void testGameRotationVector_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_100HZ);
     }
 
     public void testGameRotationVector_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ);
     }
 
     public void testGameRotationVector_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_25HZ);
     }
 
     public void testGameRotationVector_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_15HZ);
     }
 
     public void testGameRotationVector_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_10HZ);
     }
 
     public void testGameRotationVector_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_1HZ);
     }
 
     public void testGyroscopeUncalibrated_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
-                BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void testGyroscopeUncalibrated_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_200HZ);
     }
 
     public void testGyroscopeUncalibrated_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_100HZ);
     }
 
     public void testGyroscopeUncalibrated_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ);
     }
 
     public void testGyroscopeUncalibrated_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_25HZ);
     }
 
     public void testGyroscopeUncalibrated_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_15HZ);
     }
 
     public void testGyroscopeUncalibrated_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_10HZ);
     }
 
     public void testGyroscopeUncalibrated_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_5HZ);
     }
 
     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);
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_1HZ);
     }
 
     public void  testGeomagneticRotationVector_fastest() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
-                BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST);
     }
 
     public void  testLinearAcceleration_200hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_200HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_200HZ);
     }
 
     public void  testLinearAcceleration_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_100HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_100HZ);
     }
 
     public void testLinearAcceleration_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ);
     }
 
     public void testLinearAcceleration_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_25HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_25HZ);
     }
 
     public void testLinearAcceleration_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_15HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_15HZ);
     }
 
     public void testLinearAcceleration_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_10HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_10HZ);
     }
 
     public void testLinearAcceleration_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_5HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_5HZ);
     }
 
     public void testLinearAcceleration_1hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_1HZ, BATCHING_OFF);
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_1HZ);
     }
 
-    public void testLinearAcceleration_fastest_batching() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, SensorManager.SENSOR_DELAY_FASTEST,
-                BATCHING_5S);
-    }
-
-    public void testLinearAcceleration_50hz_batching() throws Throwable {
-        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_5S);
-    }
-
-    private void runSensorTest(int sensorType, int rateUs, int maxBatchReportLatencyUs)
+    private void runSensorTest(int sensorType, int rateUs)
             throws Throwable {
         TestSensorOperation op = new TestSensorOperation(this.getContext(), sensorType,
-                rateUs, maxBatchReportLatencyUs, 5, TimeUnit.SECONDS);
-        op.setDefaultVerifications();
+                rateUs, 0 /* maxBatchReportLatencyUs */, 5, TimeUnit.SECONDS);
+        op.addDefaultVerifications();
         op.setLogEvents(true);
         try {
             op.execute();
@@ -659,9 +548,7 @@
                 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);
+            String fileName = String.format("single_sensor_%s_%s.txt", sensorName, sensorRate);
             SensorStats.logStatsToFile(fileName, op.getStats());
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
index a45ad70..1c1ea1c 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.cts.helpers;
 
+import junit.framework.Assert;
+
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
@@ -23,8 +25,6 @@
 import android.hardware.SensorManager;
 import android.util.Log;
 
-import junit.framework.Assert;
-
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -213,6 +213,29 @@
     }
 
     /**
+     * Registers a listener, waits for a specific duration, calls flush, and waits for flush to
+     * complete.
+     */
+    public void runSensorAndFlush(
+            TestSensorEventListener listener,
+            long duration,
+            TimeUnit timeUnit) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return;
+        }
+
+        try {
+            registerListener(listener);
+            SensorCtsHelper.sleep(duration, timeUnit);
+            startFlush();
+            listener.waitForFlushComplete();
+        } finally {
+            unregisterListener();
+        }
+    }
+
+    /**
      * Get the sensor under test.
      */
     public Sensor getSensor() {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
new file mode 100644
index 0000000..135d674
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
@@ -0,0 +1,79 @@
+/*
+ * 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.TestSensorEventListener;
+
+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, call flush, and then run all the tests.
+ * </p>
+ */
+public class TestSensorFlushOperation extends VerifiableSensorOperation {
+    private final Long mDuration;
+    private final TimeUnit mTimeUnit;
+
+    /**
+     * 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 before calling {@code SensorManager.flush()}
+     * @param timeUnit the time unit of the duration
+     */
+    public TestSensorFlushOperation(
+            Context context,
+            int sensorType,
+            int rateUs,
+            int maxBatchReportLatencyUs,
+            long duration,
+            TimeUnit timeUnit) {
+        super(context, sensorType, rateUs, maxBatchReportLatencyUs);
+        mDuration = duration;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doExecute(TestSensorEventListener listener) {
+        mSensorManager.runSensorAndFlush(listener, mDuration, mTimeUnit);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected VerifiableSensorOperation doClone() {
+        return new TestSensorFlushOperation(
+                mContext,
+                mSensorType,
+                mRateUs,
+                mMaxBatchReportLatencyUs,
+                mDuration,
+                mTimeUnit);
+    }
+}
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
index b841eda..5982d24 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -17,25 +17,8 @@
 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 android.hardware.cts.helpers.TestSensorEventListener;
 
-import junit.framework.Assert;
-
-import java.util.Collection;
-import java.util.HashSet;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -46,21 +29,11 @@
  * 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;
+public class TestSensorOperation extends VerifiableSensorOperation {
     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}.
      *
@@ -95,109 +68,35 @@
      */
     private TestSensorOperation(Context context, int sensorType, int rateUs,
             int maxBatchReportLatencyUs, Integer eventCount, Long duration, TimeUnit timeUnit) {
-        mContext = context;
-        mSensorType = sensorType;
-        mRateUs = rateUs;
-        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+        super(context, sensorType, rateUs, 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.
+     * {@inheritDoc}
      */
     @Override
-    public void execute() {
-        getStats().addValue("sensor_name", SensorTestInformation.getSensorName(mSensorType));
-
-        ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
-        listener.setLogEvents(mLogEvents);
-
+    protected void doExecute(TestSensorEventListener listener) {
         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;
+    protected VerifiableSensorOperation doClone() {
         if (mEventCount != null) {
-            operation = new TestSensorOperation(mContext, mSensorType, mRateUs,
+            return new TestSensorOperation(mContext, mSensorType, mRateUs,
                     mMaxBatchReportLatencyUs, mEventCount);
         } else {
-            operation = new TestSensorOperation(mContext, mSensorType, mRateUs,
+            return 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/VerifiableSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
new file mode 100644
index 0000000..d0a5673
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
@@ -0,0 +1,173 @@
+/*
+ * 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 junit.framework.Assert;
+
+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.TestSensorEventListener;
+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 java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * 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 abstract class VerifiableSensorOperation extends AbstractSensorOperation {
+    protected final TestSensorManager mSensorManager;
+    protected final Context mContext;
+    protected final int mSensorType;
+    protected final int mRateUs;
+    protected final int mMaxBatchReportLatencyUs;
+
+    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
+     */
+    public VerifiableSensorOperation(
+            Context context,
+            int sensorType,
+            int rateUs,
+            int maxBatchReportLatencyUs) {
+        mContext = context;
+        mSensorType = sensorType;
+        mRateUs = rateUs;
+        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+        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 addDefaultVerifications() {
+        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));
+
+        ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
+        listener.setLogEvents(mLogEvents);
+
+        doExecute(listener);
+
+        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 VerifiableSensorOperation clone() {
+        VerifiableSensorOperation operation = doClone();
+        for (ISensorVerification verification : mVerifications) {
+            operation.addVerification(verification.clone());
+        }
+        return operation;
+    }
+
+    /**
+     * Execute operations in a {@link TestSensorManager}.
+     */
+    protected abstract void doExecute(TestSensorEventListener listener);
+
+    /**
+     * Clone the subclass operation.
+     */
+    protected abstract VerifiableSensorOperation doClone();
+
+    /**
+     * 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;
+    }
+}