Merge "add test for setLocalFosus and injectInputEvent" into klp-dev
diff --git a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
index 30a1f67..85159a8 100644
--- a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -68,9 +68,12 @@
                 reportedVersion, getVersionFromPackageManager(mActivity));
 
         assertGlVersionString(1);
-        if (detectedVersion >= 2) {
+        if (detectedVersion == 2) {
             restartActivityWithClientVersion(2);
             assertGlVersionString(2);
+        } else if (detectedVersion == 3) {
+            restartActivityWithClientVersion(3);
+            assertGlVersionString(3);
         }
     }
 
@@ -190,9 +193,6 @@
         String versionString = "" + majorVersion;
         String message = "OpenGL version string '" + mActivity.getVersionString()
                 + "' is not " + majorVersion + ".0+.";
-        if (majorVersion == 2) {
-             versionString = "(2|3)";
-        }
         assertTrue(message, Pattern.matches(".*OpenGL.*ES.*" + versionString + "\\.\\d.*",
                 mActivity.getVersionString()));
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java
index 51fd5f6..2e33a5d 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java
@@ -20,6 +20,8 @@
 import android.hardware.SensorManager;
 
 public class SensorAccelerometerTest extends SensorCommonTests {
+    private final int AXIS_COUNT = 3;
+
     @Override
     protected int getMaxFrequencySupportedInuS() {
         return 10000; // 100Hz
@@ -37,21 +39,13 @@
      */
     @Override
     public void testEventValidity() {
-        final float THRESHOLD = 0.25f; // m / s^2
-        validateSensorEvent(
-                0 /*x-axis*/,
-                0 /*y-axis*/,
-                SensorManager.STANDARD_GRAVITY /*z-axis*/,
-                THRESHOLD);
+        final float THRESHOLD = 0.5f; // m / s^2
+        validateNormForSensorEvent(SensorManager.STANDARD_GRAVITY, THRESHOLD, AXIS_COUNT);
     }
 
     @Override
-    public void testVarianceWhileStatic() {
-        final float THRESHOLD = 0.25f; // m / s^2
-        validateVarianceWhileStatic(
-                0 /*x-axis*/,
-                0 /*y-axis*/,
-                SensorManager.STANDARD_GRAVITY /*z-axis*/,
-                THRESHOLD);
+    public void testStandardDeviationWhileStatic() {
+        final float STANDARD_DEVIATION = 1f; // m / s^2
+        validateStandardDeviationWhileStatic(STANDARD_DEVIATION, AXIS_COUNT);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java
index 5cc65bb..326963c 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java
@@ -18,45 +18,34 @@
 
 import android.content.Context;
 
-import android.hardware.FlushCompleteListener;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
 
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.TestSensorManager;
+
 import android.os.PowerManager;
 
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
 
-import java.util.List;
 import android.util.Log;
 
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-import java.text.SimpleDateFormat;
-
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
+import java.util.List;
 import java.util.Random;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import junit.framework.Assert;
+
 /**
  * Class is not marked public to avoid TestRunner to pick the tests in it
  */
 abstract class SensorCommonTests extends AndroidTestCase {
     protected final String LOG_TAG = "TestRunner";
-
-    protected SensorManager mSensorManager;
-    protected Sensor mSensorUnderTest;
-    protected TestSensorListener mEventListener;
-
-    private FlushCompleteListener mFlushListener;
+    protected TestSensorManager mTestSensorManager;
     private PowerManager.WakeLock mWakeLock;
 
     protected SensorCommonTests() {}
@@ -71,15 +60,11 @@
      * Abstract test methods that sensors need to verify
      */
     public abstract void testEventValidity();
-    public abstract void testVarianceWhileStatic();
+    public abstract void testStandardDeviationWhileStatic();
 
     /**
      * Methods to control the behavior of the tests by concrete sensor tests
      */
-    protected int getWaitTimeoutInSeconds() {
-        return 30;
-    }
-
     protected int getHighNumberOfIterationsToExecute() {
         return 100;
     }
@@ -101,31 +86,27 @@
                 Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getLogTag());
         mWakeLock.acquire();
-
-        mEventListener = new TestSensorListener();
-        mFlushListener = new TestFlushListener();
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
-
-        mFlushListener = null;
-        mEventListener = null;
-        mSensorUnderTest = null;
+        if(mTestSensorManager != null) {
+            mTestSensorManager.close();
+            mTestSensorManager = null;
+        }
 
         releaseWakeLock();
     }
 
     @Override
     public void runBare() throws Throwable {
-        mSensorManager = (SensorManager) this.getContext().getSystemService(Context.SENSOR_SERVICE);
-        assertNotNull("getSystemService#Sensor_Service", mSensorManager);
+        SensorManager sensorManager = (SensorManager) this.getContext().getSystemService(Context.SENSOR_SERVICE);
+        assertNotNull("getSystemService#Sensor_Service", sensorManager);
 
-        List<Sensor> availableSensors = mSensorManager.getSensorList(this.getSensorType());
+        List<Sensor> availableSensors = sensorManager.getSensorList(this.getSensorType());
         // it is OK if there are no sensors available
         for(Sensor sensor : availableSensors) {
-            mSensorUnderTest = sensor;
+            mTestSensorManager = new TestSensorManager(this, sensorManager, sensor);
             super.runBare();
         }
     }
@@ -134,49 +115,34 @@
      * Test cases continuous mode.
      */
     public void testCanRegisterListener() {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                SensorManager.SENSOR_DELAY_NORMAL);
-        assertTrue("registerListener", result);
+        mTestSensorManager.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
     }
 
     public void testNotTriggerSensor() {
         TestTriggerListener listener = new TestTriggerListener();
-        assertFalse(
-                "requestTriggerSensor",
-                mSensorManager.requestTriggerSensor(listener, mSensorUnderTest));
+        boolean result = mTestSensorManager.getUnderlyingSensorManager().requestTriggerSensor(
+                listener,
+                mTestSensorManager.getSensorUnderTest());
+        assertFalse("requestTriggerSensor", result);
     }
 
     public void testCanReceiveEvents() {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                SensorManager.SENSOR_DELAY_NORMAL);
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(5);
+        mTestSensorManager.collectEvents(SensorManager.SENSOR_DELAY_NORMAL, 5);
     }
 
     public void testMaxFrequency() {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                this.getMaxFrequencySupportedInuS());
-        assertTrue("registerListener", result);
+        // TODO: verify that events do arrive at the proper rate
+        mTestSensorManager.registerListener(this.getMaxFrequencySupportedInuS());
     }
 
     public void testEventsArriveInOrder() {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                SensorManager.SENSOR_DELAY_FASTEST);
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(100);
-
-        SensorEventForTest[] events = mEventListener.getAllEvents();
+        // TODO: test for other sensor frequencies, rely on helper test classes for sensors
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+                SensorManager.SENSOR_DELAY_FASTEST,
+                100);
         for(int i = 1; i < events.length; ++i) {
-            long previousTimestamp = events[i-1].getTimestamp();
-            long timestamp = events[i].getTimestamp();
+            long previousTimestamp = events[i-1].timestamp;
+            long timestamp = events[i].timestamp;
             assertTrue(
                     String.format("[timestamp:%d] %d >= %d", i, previousTimestamp, timestamp),
                     previousTimestamp < timestamp);
@@ -184,17 +150,7 @@
     }
 
     public void testStartStopRepeatedly() {
-        for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
-            String iterationInfo = String.format("registerListener:%d", i);
-            boolean result = mSensorManager.registerListener(
-                    mEventListener,
-                    mSensorUnderTest,
-                    SensorManager.SENSOR_DELAY_FASTEST);
-            assertTrue(iterationInfo, result);
-            mEventListener.waitForEvents(1, iterationInfo);
-
-            mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
-        }
+        validateRegisterUnregisterRepeteadly(mTestSensorManager);
     }
 
     public void testUpdateRate() {
@@ -221,33 +177,87 @@
                     rate = this.getMaxFrequencySupportedInuS() * generator.nextInt(10);
             }
 
-            String iterationInfo = String.format("registerListener:%d, rate:%d", i, rate);
-            assertTrue(
-                    iterationInfo,
-                    mSensorManager.registerListener(mEventListener, mSensorUnderTest, rate));
-
-            mEventListener.waitForEvents(generator.nextInt(5) + 1, iterationInfo);
-            mEventListener.clearEvents();
-
-            mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
+            // TODO: check that the rate has indeed changed
+            mTestSensorManager.collectEvents(
+                    rate,
+                    generator.nextInt(5) + 1,
+                    String.format("iteration:%d, rate:%d", i, rate));
         }
     }
 
-    public void testSeveralClients() throws InterruptedException {
-        ArrayList<Thread> threads = new ArrayList<Thread>();
-        for(int i = 0; i < this.getNumberOfThreadsToUse(); ++i) {
-            threads.add(new Thread() {
-                @Override
-                public void run() {
-                    testStartStopRepeatedly();
-                }
-            });
-        }
+    public void testOneClientSeveralThreads() throws InterruptedException {
+        Runnable operation = new Runnable() {
+            @Override
+            public void run() {
+                validateRegisterUnregisterRepeteadly(mTestSensorManager);
+            }
+        };
+        SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+    }
 
-        while(!threads.isEmpty()) {
-            Thread thread = threads.remove(0);
-            thread.join();
-        }
+    public void testSeveralClients() throws InterruptedException {
+        final Assert assertionObject = this;
+        Runnable operation = new Runnable() {
+            @Override
+            public void run() {
+                TestSensorManager testSensorManager = new TestSensorManager(
+                        assertionObject,
+                        mTestSensorManager.getUnderlyingSensorManager(),
+                        mTestSensorManager.getSensorUnderTest());
+                validateRegisterUnregisterRepeteadly(testSensorManager);
+            }
+        };
+        SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+    }
+
+    public void testStoppingOtherClients() {
+        // TODO: use a higher test abstraction and move these to integration tests
+        final int EVENT_COUNT = 1;
+        final int SECOND_EVENT_COUNT = 5;
+        TestSensorManager sensorManager2 = new TestSensorManager(
+                this,
+                mTestSensorManager.getUnderlyingSensorManager(),
+                mTestSensorManager.getSensorUnderTest());
+
+        mTestSensorManager.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
+
+        // is receiving events
+        mTestSensorManager.getEvents(EVENT_COUNT);
+
+        // operate in a different client
+        sensorManager2.collectEvents(SensorManager.SENSOR_DELAY_FASTEST, SECOND_EVENT_COUNT);
+
+        // verify first client is still operating
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+        assertTrue(
+                String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+                events.length >= EVENT_COUNT);
+    }
+
+    public void testStoppingOtherClientsBatching() {
+        final int EVENT_COUNT = 1;
+        final int SECOND_EVENT_COUNT = 5;
+        TestSensorManager sensorManager2 = new TestSensorManager(
+                this,
+                mTestSensorManager.getUnderlyingSensorManager(),
+                mTestSensorManager.getSensorUnderTest());
+
+        mTestSensorManager.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
+
+        // is receiving events
+        mTestSensorManager.getEvents(EVENT_COUNT);
+
+        // operate in a different client
+        sensorManager2.collectBatchEvents(
+                SensorManager.SENSOR_DELAY_FASTEST,
+                SensorCtsHelper.getSecondsAsMicroSeconds(1),
+                SECOND_EVENT_COUNT);
+
+        // verify first client is still operating
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+        assertTrue(
+                String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+                events.length >= EVENT_COUNT);
     }
 
     /**
@@ -255,50 +265,52 @@
      */
     public void testRegisterForBatchingZeroReport() {
         releaseWakeLock();
-
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                SensorManager.SENSOR_DELAY_NORMAL,
-                0 /*maxBatchReportLatencyUs*/,
-                0 /*reservedFlags*/,
-                mFlushListener);
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(10);
+        // TODO: use test wrappers to verify for reportLatency ==0 !=0
+        mTestSensorManager.collectBatchEvents(SensorManager.SENSOR_DELAY_NORMAL, 0, 10);
     }
 
     public void testCanReceiveBatchEvents() {
         releaseWakeLock();
-
-        // TODO: refactor out common code across tests that register for events and do post-process
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
+        mTestSensorManager.collectBatchEvents(
                 SensorManager.SENSOR_DELAY_NORMAL,
-                5 * 1000000 /*maxBatchReportLatencyUs*/,
-                0 /*reservedFlags*/,
-                mFlushListener);
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(10);
+                SensorCtsHelper.getSecondsAsMicroSeconds(5),
+                10 /*eventCount*/);
+    }
+
+    /**
+     * Regress:
+     * -b/10790905
+     */
+    public void ignore_testBatchingReportLatency() {
+        long startTime = SystemClock.elapsedRealtimeNanos();
+        // TODO: define the sensor frequency per sensor
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectBatchEvents(
+                SensorCtsHelper.getSecondsAsMicroSeconds(1),
+                SensorCtsHelper.getSecondsAsMicroSeconds(5),
+                1 /*eventCount*/);
+        long elapsedTime = SystemClock.elapsedRealtimeNanos() - startTime;
+        long expectedTime =
+                TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS) +
+                TimeUnit.NANOSECONDS.convert(500, TimeUnit.MILLISECONDS);
+
+        // TODO: ensure the proper batching time considers the size of the FIFO (fifoMaxEventCount),
+        //       and make sure that no other application is registered
+        assertTrue(
+                String.format("WaitTime| expected:%d, actual:%d", expectedTime, elapsedTime),
+                elapsedTime <= expectedTime);
     }
 
     public void testBatchEventsArriveInOrder() {
         releaseWakeLock();
 
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
+        // TODO: identify if we can reuse code from the non-batching case, same for other batch tests
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectBatchEvents(
                 SensorManager.SENSOR_DELAY_NORMAL,
-                5 * 1000000 /*maxBatchReportLatencyUs*/,
-                0 /*reservedFlags*/,
-                mFlushListener);
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(100);
-
-        SensorEventForTest[] events = mEventListener.getAllEvents();
+                SensorCtsHelper.getSecondsAsMicroSeconds(5),
+                100);
         for(int i = 1; i < events.length; ++i) {
-            long previousTimestamp = events[i-1].getTimestamp();
-            long timestamp = events[i].getTimestamp();
+            long previousTimestamp = events[i-1].timestamp;
+            long timestamp = events[i].timestamp;
             assertTrue(
                     String.format("[timestamp:%d] %d >= %d", i, previousTimestamp, timestamp),
                     previousTimestamp < timestamp);
@@ -307,22 +319,7 @@
 
     public void testStartStopBatchingRepeatedly() {
         releaseWakeLock();
-
-        for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
-            String iterationInfo = String.format("registerListener:%d", i);
-            boolean result = mSensorManager.registerListener(
-                    mEventListener,
-                    mSensorUnderTest,
-                    SensorManager.SENSOR_DELAY_FASTEST,
-                    5 * 1000000 /*maxBatchReportLatencyUs*/,
-                    0 /*reservedFlags*/,
-                    mFlushListener);
-
-            assertTrue(iterationInfo, result);
-            mEventListener.waitForEvents(5, iterationInfo);
-
-            mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
-        }
+        validateRegisterUnregisterRepeteadlyBatching(mTestSensorManager);
     }
 
     public void testUpdateBatchRate() {
@@ -351,41 +348,91 @@
                     rate = this.getMaxFrequencySupportedInuS() * generator.nextInt(10);
             }
 
-            String iterationInfo = String.format("registerListener:%d, rate:%d", i, rate);
-            boolean result = mSensorManager.registerListener(
-                    mEventListener,
-                    mSensorUnderTest,
+            String iterationInfo = String.format("iteration:%d, rate:%d", i, rate);
+            mTestSensorManager.collectBatchEvents(
                     rate,
-                    generator.nextInt(5 * 1000000),
-                    0 /*reservedFlags*/,
-                    mFlushListener);
-            assertTrue(iterationInfo, result);
-
-            mEventListener.waitForEvents(generator.nextInt(5) + 1, iterationInfo);
-            mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
-            mEventListener.clearEvents();
+                    generator.nextInt(SensorCtsHelper.getSecondsAsMicroSeconds(5)),
+                    generator.nextInt(5) + 1,
+                    iterationInfo);
         }
     }
 
-    public void testSeveralClientsBatching() {
-        ArrayList<Thread> threads = new ArrayList<Thread>();
-        for(int i = 0; i < this.getNumberOfThreadsToUse(); ++i) {
-            threads.add(new Thread() {
-                @Override
-                public void run() {
-                    testStartStopBatchingRepeatedly();
-                }
-            });
-        }
-
-        while(!threads.isEmpty()) {
-            Thread thread = threads.remove(0);
-            try {
-                thread.join();
-            } catch(InterruptedException e) {
-                // just continue
+    public void testOneClientSeveralThreadsBatching() throws InterruptedException {
+        Runnable operation = new Runnable() {
+            @Override
+            public void run() {
+                validateRegisterUnregisterRepeteadlyBatching(mTestSensorManager);
             }
-        }
+        };
+        SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+    }
+
+    public void testSeveralClientsBatching() throws InterruptedException {
+        final Assert assertionObject = this;
+        Runnable operation = new Runnable() {
+            @Override
+            public void run() {
+                TestSensorManager testSensorManager = new TestSensorManager(
+                        assertionObject,
+                        mTestSensorManager.getUnderlyingSensorManager(),
+                        mTestSensorManager.getSensorUnderTest());
+                validateRegisterUnregisterRepeteadlyBatching(testSensorManager);
+            }
+        };
+        SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+    }
+
+    public void testBatchingStoppingOtherClients() {
+        final int EVENT_COUNT = 1;
+        final int SECOND_EVENT_COUNT = 5;
+        TestSensorManager sensorManager2 = new TestSensorManager(
+                this,
+                mTestSensorManager.getUnderlyingSensorManager(),
+                mTestSensorManager.getSensorUnderTest());
+
+        mTestSensorManager.registerBatchListener(
+                SensorManager.SENSOR_DELAY_NORMAL,
+                SensorCtsHelper.getSecondsAsMicroSeconds(5));
+
+        // is receiving events
+        mTestSensorManager.getEvents(EVENT_COUNT);
+
+        // operate in a different client
+        sensorManager2.collectEvents(SensorManager.SENSOR_DELAY_FASTEST, SECOND_EVENT_COUNT);
+
+        // verify first client is still operating
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+        assertTrue(
+                String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+                events.length >= EVENT_COUNT);
+    }
+
+    public void testBatchingStoppingOtherClientsBatching() {
+        final int EVENT_COUNT = 1;
+        final int SECOND_EVENT_COUNT = 5;
+        TestSensorManager sensorManager2 = new TestSensorManager(
+                this,
+                mTestSensorManager.getUnderlyingSensorManager(),
+                mTestSensorManager.getSensorUnderTest());
+
+        mTestSensorManager.registerBatchListener(
+                SensorManager.SENSOR_DELAY_NORMAL,
+                SensorCtsHelper.getSecondsAsMicroSeconds(5));
+
+        // is receiving events
+        mTestSensorManager.getEvents(EVENT_COUNT);
+
+        // operate in a different client
+        sensorManager2.collectBatchEvents(
+                SensorManager.SENSOR_DELAY_FASTEST,
+                SensorCtsHelper.getSecondsAsMicroSeconds(1),
+                SECOND_EVENT_COUNT);
+
+        // verify first client is still operating
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+        assertTrue(
+                String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+                events.length >= EVENT_COUNT);
     }
 
     /**
@@ -394,33 +441,21 @@
     public void testEventJittering() {
         final long EXPECTED_TIMESTAMP_NS = this.getMaxFrequencySupportedInuS() * 1000;
         final long THRESHOLD_IN_NS = EXPECTED_TIMESTAMP_NS / 10; // 10%
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                this.getMaxFrequencySupportedInuS());
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(100);
 
-        SensorEventForTest[] events = mEventListener.getAllEvents();
-        ArrayList<Long> timestampDeltas = new ArrayList<Long>();
-        for(int i = 1; i < events.length; ++i) {
-            long previousTimestamp = events[i-1].getTimestamp();
-            long timestamp = events[i].getTimestamp();
-            long delta = timestamp - previousTimestamp;
-            long jitterValue = Math.abs(EXPECTED_TIMESTAMP_NS - delta);
-            timestampDeltas.add(jitterValue);
-        }
-
-        Collections.sort(timestampDeltas);
-        long percentile95InNs = timestampDeltas.get(95);
-        long actualPercentValue = (percentile95InNs * 100) / EXPECTED_TIMESTAMP_NS;
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+                this.getMaxFrequencySupportedInuS(),
+                100);
+        ArrayList<Double> jitterValues = new ArrayList<Double>();
+        double jitterMean = SensorCtsHelper.getJitterMean(events, jitterValues);
+        double percentile95InNs = SensorCtsHelper.get95PercentileValue(jitterValues);
 
         if(percentile95InNs > THRESHOLD_IN_NS) {
-            for(long jitter : timestampDeltas) {
-                Log.e(LOG_TAG, "Jittering delta: " + jitter);
+            for(double jitter : jitterValues) {
+                Log.e(LOG_TAG, "Jitter: " + jitter);
             }
+            double actualPercentValue = (percentile95InNs * 100) / jitterMean;
             String message = String.format(
-                    "95%%Jitter| 10%%:%dns, observed:%dns(%d%%)",
+                    "95%% Jitter| 10%%:%dns, actual:%fns(%.2f%%)",
                     THRESHOLD_IN_NS,
                     percentile95InNs,
                     actualPercentValue);
@@ -428,6 +463,29 @@
         }
     }
 
+    public void testFrequencyAccuracy() {
+        final long EXPECTED_TIMESTAMP_NS = this.getMaxFrequencySupportedInuS() * 1000;
+        final long THRESHOLD_IN_NS = EXPECTED_TIMESTAMP_NS / 10; // 10%
+
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+                this.getMaxFrequencySupportedInuS(),
+                100);
+        ArrayList<Long> timestampDelayValues = new ArrayList<Long>();
+        Double frequencyMean = SensorCtsHelper.getAverageTimestampDelayWithValues(
+                events,
+                timestampDelayValues);
+        if(Math.abs(EXPECTED_TIMESTAMP_NS - frequencyMean) > THRESHOLD_IN_NS) {
+            for(long value : timestampDelayValues) {
+                Log.e(LOG_TAG, "TimestampDelay: " + value);
+            }
+            String message = String.format(
+                    "Frequency| expected:%d, actual:%f",
+                    EXPECTED_TIMESTAMP_NS,
+                    frequencyMean);
+            fail(message);
+        }
+    }
+
     /**
      * Private helpers.
      */
@@ -444,252 +502,84 @@
         }
     }
 
-    private void collectBugreport() {
-        String commands[] = new String[] {
-                "dumpstate",
-                "dumpsys",
-                "logcat -d -v threadtime",
-                "exit"
-        };
-
-        SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-y_H:m:s.S");
-        String outputFile = String.format(
-                "%s/%s_%s",
-                this.getLogTag(),
-                "/sdcard/Download",
-                dateFormat.format(new Date()));
-
-        DataOutputStream processOutput = null;
-        try {
-            Process process = Runtime.getRuntime().exec("/system/bin/sh -");
-            processOutput = new DataOutputStream(process.getOutputStream());
-
-            for(String command : commands) {
-                processOutput.writeBytes(String.format("%s >> %s\n", command, outputFile));
-            }
-
-            processOutput.flush();
-            process.waitFor();
-
-            Log.d(this.getLogTag(), String.format("Bug-Report collected at: %s", outputFile));
-        } catch (IOException e) {
-            fail("Unable to collect Bug Report. " + e.toString());
-        } catch (InterruptedException e) {
-            fail("Unable to collect Bug Report. " + e.toString());
-        } finally {
-            if(processOutput != null) {
-                try {
-                    processOutput.close();
-                } catch(IOException e) {}
-            }
-        }
-    }
-
     /**
      * Test method helper implementations
      */
-    protected void validateSensorEvent(
-            float expectedX,
-            float expectedY,
-            float expectedZ,
-            float threshold) {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                SensorManager.SENSOR_DELAY_FASTEST);
+    protected void validateNormForSensorEvent(float reference, float threshold, int axisCount) {
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+                SensorManager.SENSOR_DELAY_FASTEST,
+                1);
+        TestSensorManager.SensorEventForTest event = events[0];
 
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(1);
-        SensorEventForTest event = mEventListener.getLastEvent();
+        StringBuilder valuesBuilder = new StringBuilder();
+        double norm = 0.0;
+        for(int i = 0; i < axisCount; ++i) {
+            float value = event.values[i];
+            norm += Math.pow(value, 2);
 
-        float xValue = event.getX();
-        assertTrue(
-                String.format("x-axis| expected:%f, actual:%f, threshold:%f", expectedX, xValue, threshold),
-                Math.abs(expectedX - xValue) <= threshold);
+            valuesBuilder.append(value);
+            valuesBuilder.append(", ");
+        }
+        norm = Math.sqrt(norm);
 
-        float yValue = event.getY();
-        assertTrue(
-                String.format("y-axis| expected:%f, actual:%f, threshold:%f", expectedY, yValue, threshold),
-                Math.abs(expectedY - yValue) <= threshold);
-
-        float zValue = event.getZ();
-        assertTrue(
-                String.format("z-axis| expected:%f, actual:%f, threshold:%f", expectedZ, zValue, threshold),
-                Math.abs(expectedZ - zValue) <= threshold);
+        String message = String.format(
+                "Norm| expected:%f, threshold:%f, actual:%f (%s)",
+                reference,
+                threshold,
+                norm,
+                valuesBuilder.toString());
+        assertTrue(message, Math.abs(reference - norm) <= threshold);
     }
 
-    protected void validateVarianceWhileStatic(
-            float referenceX,
-            float referenceY,
-            float referenceZ,
-            float threshold) {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                this.getMaxFrequencySupportedInuS());
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(100);
-
-        SensorEventForTest[] events = mEventListener.getAllEvents();
-        ArrayList<Float> deltaValuesX = new ArrayList<Float>();
-        ArrayList<Float> deltaValuesY = new ArrayList<Float>();
-        ArrayList<Float> deltaValuesZ = new ArrayList<Float>();
-        for(int i = 0; i < events.length; ++i) {
-            SensorEventForTest event = events[i];
-            deltaValuesX.add(Math.abs(event.getX() - referenceX));
-            deltaValuesY.add(Math.abs(event.getY() - referenceY));
-            deltaValuesZ.add(Math.abs(event.getZ() - referenceZ));
+    protected void validateRegisterUnregisterRepeteadly(TestSensorManager testSensorManager) {
+        for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
+            String iterationInfo = String.format("iteration:%d", i);
+            testSensorManager.collectEvents(SensorManager.SENSOR_DELAY_FASTEST, 1, iterationInfo);
         }
+    }
 
-        Collections.sort(deltaValuesX);
-        float percentile95X = deltaValuesX.get(95);
-        if(percentile95X > threshold) {
-            for(float valueX : deltaValuesX) {
-                Log.e(LOG_TAG, "Variance|X delta: " + valueX);
-            }
-            String message = String.format(
-                    "95%%Variance|X expected:%f, observed:%f",
-                    threshold,
-                    percentile95X);
-            fail(message);
+    protected void validateRegisterUnregisterRepeteadlyBatching(
+            TestSensorManager testSensorManager) {
+        // TODO: refactor if allowed with test wrapper abstractions
+        for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
+            testSensorManager.collectBatchEvents(
+                    SensorManager.SENSOR_DELAY_FASTEST,
+                    SensorCtsHelper.getSecondsAsMicroSeconds(5),
+                    5 /*eventCont*/,
+                    String.format("iteration:%d", i));
         }
+    }
 
-        Collections.sort(deltaValuesY);
-        float percentile95Y = deltaValuesY.get(95);
-        if(percentile95Y > threshold) {
-            for(float valueY : deltaValuesY) {
-                Log.e(LOG_TAG, "Variance|Y delta: " + valueY);
-            }
-            String message = String.format(
-                    "95%%Variance|Y expected:%f, observed:%f",
-                    threshold,
-                    percentile95Y);
-            fail(message);
-        }
+    protected void validateStandardDeviationWhileStatic(
+            float expectedStandardDeviation,
+            int axisCount) {
+        // TODO: refactor the report parameter with test wrappers if available
+        TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+                this.getMaxFrequencySupportedInuS(),
+                100);
 
-        Collections.sort(deltaValuesZ);
-        float percentile95Z = deltaValuesZ.get(95);
-        if(percentile95Z > threshold) {
-            for(float valueZ : deltaValuesZ) {
-                Log.e(LOG_TAG, "Variance|Z delta: " + valueZ);
+        for(int i = 0; i < axisCount; ++i) {
+            ArrayList<Float> values = new ArrayList<Float>();
+            for(TestSensorManager.SensorEventForTest event : events) {
+                values.add(event.values[i]);
             }
+
+            double standardDeviation = SensorCtsHelper.getStandardDeviation(values);
             String message = String.format(
-                    "95%%Variance|Z expected:%f, observed:%f",
-                    threshold,
-                    percentile95Z);
-            fail(message);
+                    "StandardDeviation| axis:%d, expected:%f, actual:%f",
+                    i,
+                    expectedStandardDeviation,
+                    standardDeviation);
+            assertTrue(message, standardDeviation <= expectedStandardDeviation);
         }
     }
 
     /**
      * Private class definitions to support test of event handlers.
      */
-    protected class SensorEventForTest {
-        private Sensor mSensor;
-        private long mTimestamp;
-        private int mAccuracy;
-
-        private float mValueX;
-        private float mValueY;
-        private float mValueZ;
-
-        public SensorEventForTest(SensorEvent event) {
-            mSensor = event.sensor;
-            mTimestamp = event.timestamp;
-            mAccuracy = event.accuracy;
-            mValueX = event.values[0];
-            mValueY = event.values[1];
-            mValueZ = event.values[2];
-        }
-
-        public Sensor getSensor() {
-            return mSensor;
-        }
-
-        public int getAccuracy() {
-            return mAccuracy;
-        }
-
-        public float getX() {
-            return mValueX;
-        }
-
-        public float getY() {
-            return mValueY;
-        }
-
-        public float getZ() {
-            return mValueZ;
-        }
-
-        public long getTimestamp() {
-            return mTimestamp;
-        }
-    }
-
-    protected class TestSensorListener implements SensorEventListener {
-        private final ConcurrentLinkedDeque<SensorEventForTest> mSensorEventsList =
-                new ConcurrentLinkedDeque<SensorEventForTest>();
-        private volatile CountDownLatch mEventLatch;
-
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            // copy the event because there is no better way to do this in the platform
-            mSensorEventsList.addLast(new SensorEventForTest(event));
-
-            CountDownLatch latch = mEventLatch;
-            if(latch != null) {
-                latch.countDown();
-            }
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-        }
-
-        public void waitForEvents(int eventCount) {
-            waitForEvents(eventCount, null);
-        }
-
-        public void waitForEvents(int eventCount, String timeoutInfo) {
-            mEventLatch = new CountDownLatch(eventCount);
-            try {
-                boolean awaitCompleted = mEventLatch.await(getWaitTimeoutInSeconds(), TimeUnit.SECONDS);
-                if(!awaitCompleted) {
-                    collectBugreport();
-                }
-
-                String assertMessage = String.format(
-                        "WaitForEvents:%d, available:%d, %s",
-                        eventCount,
-                        mSensorEventsList.size(),
-                        timeoutInfo);
-                assertTrue(assertMessage, awaitCompleted);
-            } catch(InterruptedException e) { }
-        }
-
-        public SensorEventForTest getLastEvent() {
-            return mSensorEventsList.getLast();
-        }
-
-        public SensorEventForTest[] getAllEvents() {
-            return mSensorEventsList.toArray(new SensorEventForTest[0]);
-        }
-
-        public void clearEvents() {
-            mSensorEventsList.clear();
-        }
-    }
-
     private class TestTriggerListener extends TriggerEventListener {
         @Override
         public void onTrigger(TriggerEvent event) {
         }
     }
-
-    private class TestFlushListener implements FlushCompleteListener {
-        @Override
-        public void onFlushCompleted(Sensor sensor) {
-        }
-    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java
index d3d72a3..b7c082e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java
@@ -19,6 +19,8 @@
 import android.hardware.Sensor;
 
 public class SensorGyroscopeTest extends SensorCommonTests {
+    private final int AXIS_COUNT = 3;
+
     @Override
     protected int getMaxFrequencySupportedInuS() {
         return 10000; // 100Hz
@@ -32,20 +34,12 @@
     @Override
     public void testEventValidity() {
         final float THRESHOLD = 0.1f; // dps
-        validateSensorEvent(
-                0 /*x-axis*/,
-                0 /*y-axis*/,
-                0 /*z-axis*/,
-                THRESHOLD);
+        validateNormForSensorEvent(0 /*reference*/, THRESHOLD, AXIS_COUNT);
     }
 
     @Override
-    public void testVarianceWhileStatic() {
-        final float THRESHOLD = 0.1f; // dps
-        validateVarianceWhileStatic(
-                0 /*x-axis*/,
-                0 /*y-axis*/,
-                0 /*z-axis*/,
-                THRESHOLD);
+    public void testStandardDeviationWhileStatic() {
+        final float STANDARD_DEVIATION = 0.5f; // dps
+        validateStandardDeviationWhileStatic(STANDARD_DEVIATION, AXIS_COUNT);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
new file mode 100644
index 0000000..f764c6c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008 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.TestSensorManager;
+
+import android.os.PowerManager;
+
+import android.test.AndroidTestCase;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class SensorIntegrationTests extends AndroidTestCase {
+    protected final String LOG_TAG = "SensorIntegrationTests";
+    private PowerManager.WakeLock mWakeLock;
+    private SensorManager mSensorManager;
+
+    /**
+     * Test execution methods
+     */
+    @Override
+    protected void setUp() throws Exception {
+        PowerManager powerManager = (PowerManager) this.getContext().getSystemService(
+                Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
+        mWakeLock.acquire();
+
+        mSensorManager = (SensorManager) this.getContext().getSystemService(
+                Context.SENSOR_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mSensorManager = null;
+
+        mWakeLock.release();
+        mWakeLock = null;
+    }
+
+    /**
+     * Test cases.
+     */
+    public void testBatchAndFlush() throws InterruptedException {
+        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        for(Sensor sensor : sensorList) {
+            // skip all non-continuous mode sensors.
+            switch(sensor.getType()) {
+                case Sensor.TYPE_SIGNIFICANT_MOTION:
+                case Sensor.TYPE_STEP_COUNTER:
+                case Sensor.TYPE_STEP_DETECTOR:
+                case Sensor.TYPE_LIGHT:
+                case Sensor.TYPE_PROXIMITY:
+                case Sensor.TYPE_AMBIENT_TEMPERATURE:
+                    continue;
+            }
+
+            TestSensorManager sensorManager = new TestSensorManager(this, mSensorManager, sensor);
+            sensorManager.registerBatchListener(SensorManager.SENSOR_DELAY_NORMAL, 0);
+
+            // wait for 25 events and call flush
+            sensorManager.getEvents(25);
+            sensorManager.waitForFlush();
+
+            sensorManager.unregisterListener();
+        }
+    }
+
+    /**
+     * Regress:
+     * - b/10641388
+     */
+    public void testAccelerometerDoesNotStopGyroscope() {
+        validateSensorCanBeStoppedIndependently(Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE);
+    }
+
+    public void testAccelerometerDoesNotStopMagnetometer() {
+        validateSensorCanBeStoppedIndependently(
+                Sensor.TYPE_ACCELEROMETER,
+                Sensor.TYPE_MAGNETIC_FIELD);
+    }
+
+    public void testGyroscopeDoesNotStopAccelerometer() {
+        validateSensorCanBeStoppedIndependently(Sensor.TYPE_GYROSCOPE, Sensor.TYPE_ACCELEROMETER);
+    }
+
+    public void testGyroscopeDoesNotStopMagnetometer() {
+        validateSensorCanBeStoppedIndependently(Sensor.TYPE_GYROSCOPE, Sensor.TYPE_MAGNETIC_FIELD);
+    }
+
+    public void testMagnetometerDoesNotStopAccelerometer() {
+        validateSensorCanBeStoppedIndependently(
+                Sensor.TYPE_MAGNETIC_FIELD,
+                Sensor.TYPE_ACCELEROMETER);
+    }
+
+    public void testMagnetometerDoesNotStopGyroscope() {
+        validateSensorCanBeStoppedIndependently(Sensor.TYPE_MAGNETIC_FIELD, Sensor.TYPE_GYROSCOPE);
+    }
+
+    /**
+     * Private methods for sensor validation.
+     */
+    public void validateSensorCanBeStoppedIndependently(int sensorTypeTester, int sensorTypeTestee) {
+        // if any of the required sensors is not supported, skip the test
+        Sensor sensorTester = mSensorManager.getDefaultSensor(sensorTypeTester);
+        if(sensorTester == null) {
+            return;
+        }
+        Sensor sensorTestee = mSensorManager.getDefaultSensor(sensorTypeTestee);
+        if(sensorTestee == null) {
+            return;
+        }
+
+        TestSensorManager tester = new TestSensorManager(this, mSensorManager, sensorTester);
+        tester.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
+
+        TestSensorManager testee = new TestSensorManager(this, mSensorManager, sensorTestee);
+        testee.registerBatchListener(
+                (int) TimeUnit.MICROSECONDS.convert(200, TimeUnit.MILLISECONDS),
+                SensorCtsHelper.getSecondsAsMicroSeconds(10));
+
+        testee.getEvents(10);
+        tester.getEvents(5);
+
+        tester.unregisterListener();
+        testee.getEvents(5);
+
+        // clean up
+        tester.close();
+        testee.close();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java
index 10cabb8..e4041d6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java
@@ -19,12 +19,9 @@
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
 public class SensorMagneticFieldTest extends SensorCommonTests {
+    private final int AXIS_COUNT = 3;
+
     @Override
     protected int getMaxFrequencySupportedInuS() {
         return 100000; // 10Hz
@@ -37,73 +34,15 @@
 
     @Override
     public void testEventValidity() {
-        validateSensorEvent(
-                0 /*x-axis*/,
-                0 /*y-axis*/,
-                0 /*z-axis*/,
-                SensorManager.MAGNETIC_FIELD_EARTH_MAX);
+        validateNormForSensorEvent(
+                SensorManager.MAGNETIC_FIELD_EARTH_MAX,
+                SensorManager.MAGNETIC_FIELD_EARTH_MIN,
+                AXIS_COUNT);
     }
 
     @Override
-    public void testVarianceWhileStatic() {
-        float THRESHOLD_IN_UT = SensorManager.MAGNETIC_FIELD_EARTH_MAX / 10;
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                this.getMaxFrequencySupportedInuS());
-        assertTrue("registerListener", result);
-        mEventListener.waitForEvents(100);
-
-        SensorEventForTest[] events = mEventListener.getAllEvents();
-        ArrayList<Float> deltaValuesX = new ArrayList<Float>();
-        ArrayList<Float> deltaValuesY = new ArrayList<Float>();
-        ArrayList<Float> deltaValuesZ = new ArrayList<Float>();
-        for(int i = 1; i < events.length; ++i) {
-            SensorEventForTest previousEvent = events[i-1];
-            SensorEventForTest event = events[i];
-
-            deltaValuesX.add(Math.abs(event.getX() - previousEvent.getX()));
-            deltaValuesY.add(Math.abs(event.getY() - previousEvent.getY()));
-            deltaValuesZ.add(Math.abs(event.getZ() - previousEvent.getZ()));
-        }
-
-        Collections.sort(deltaValuesX);
-        float percentile95X = deltaValuesX.get(95);
-        if(percentile95X > THRESHOLD_IN_UT) {
-            for(float valueX : deltaValuesX) {
-                Log.e(LOG_TAG, "Variance|X delta: " + valueX);
-            }
-            String message = String.format(
-                    "95%%Variance|X expected:%f, observed:%f",
-                    THRESHOLD_IN_UT,
-                    percentile95X);
-            fail(message);
-        }
-
-        Collections.sort(deltaValuesY);
-        float percentile95Y = deltaValuesY.get(95);
-        if(percentile95Y > THRESHOLD_IN_UT) {
-            for(float valueY : deltaValuesY) {
-                Log.e(LOG_TAG, "Variance|Y delta: " + valueY);
-            }
-            String message = String.format(
-                    "95%%Variance|Y expected:%f, observed:%f",
-                    THRESHOLD_IN_UT,
-                    percentile95Y);
-            fail(message);
-        }
-
-        Collections.sort(deltaValuesZ);
-        float percentile95Z = deltaValuesZ.get(95);
-        if(percentile95Z > THRESHOLD_IN_UT) {
-            for(float valueZ : deltaValuesZ) {
-                Log.e(LOG_TAG, "Variance|Z delta: " + valueZ);
-            }
-            String message = String.format(
-                    "95%%Variance|Z expected:%f, observed:%f",
-                    THRESHOLD_IN_UT,
-                    percentile95Z);
-            fail(message);
-        }
+    public void testStandardDeviationWhileStatic() {
+        final float STANDARD_DEVIATION = 2f; // uT
+        validateStandardDeviationWhileStatic(STANDARD_DEVIATION, AXIS_COUNT);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 4af2a45..a0f1a96 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -23,10 +23,10 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.hardware.FlushCompleteListener;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
+import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
@@ -149,7 +149,8 @@
             }
 
             final CountDownLatch eventReceived = new CountDownLatch(25);
-            SensorEventListener listener = new SensorEventListener() {
+            final CountDownLatch flushReceived = new CountDownLatch(1);
+            SensorEventListener2 listener = new SensorEventListener2() {
                 @Override
                 public void onSensorChanged(SensorEvent event) {
                     eventReceived.countDown();
@@ -158,22 +159,18 @@
                 @Override
                 public void onAccuracyChanged(Sensor sensor, int accuracy) {
                 }
-            };
 
-            final CountDownLatch flushReceived = new CountDownLatch(1);
-            FlushCompleteListener flushCompleteListener = new FlushCompleteListener() {
                 @Override
                 public void onFlushCompleted(Sensor sensor) {
                     flushReceived.countDown();
                 }
             };
             boolean result = mSensorManager.registerListener(listener, sensor,
-                                            SensorManager.SENSOR_DELAY_NORMAL, 10000000, 0,
-                                            flushCompleteListener);
+                                            SensorManager.SENSOR_DELAY_NORMAL, 10000000);
             assertTrue(result);
             // Wait for 25 events and call flush.
             eventReceived.await();
-            result = mSensorManager.flush(sensor);
+            result = mSensorManager.flush(listener);
             assertTrue(result);
             flushReceived.await();
             mSensorManager.unregisterListener(listener);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
new file mode 100644
index 0000000..5abdd06
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.cts.helpers;
+
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Set of static helper methods for CTS tests.
+ */
+public class SensorCtsHelper {
+    /**
+     * This is an static class.
+     */
+    private SensorCtsHelper() {}
+
+    public static <TValue extends Comparable> TValue get95PercentileValue(
+            Collection<TValue> collection) {
+        validateCollection(collection);
+
+        ArrayList<TValue> arrayCopy = new ArrayList<TValue>(collection);
+        Collections.sort(arrayCopy);
+
+        // zero-based array index
+        int arrayIndex = (int)(arrayCopy.size() * 0.95) - 1;
+        if(arrayIndex < 0) {
+            arrayIndex = 0;
+        }
+
+        return arrayCopy.get(arrayIndex);
+    }
+
+    // TODO: are there any internal libraries for this?
+    public static <TValue extends Number> double getMean(Collection<TValue> collection) {
+        validateCollection(collection);
+
+        double sum = 0.0;
+        for(TValue value : collection) {
+            sum += value.doubleValue();
+        }
+        return sum / collection.size();
+    }
+
+    public static <TValue extends Number> double getVariance(Collection<TValue> collection) {
+        validateCollection(collection);
+
+        double mean = getMean(collection);
+        ArrayList<Double> squaredDifferences = new ArrayList<Double>();
+        for(TValue value : collection) {
+            double difference = mean - value.doubleValue();
+            squaredDifferences.add(Math.pow(difference, 2));
+        }
+
+        double variance = getMean(squaredDifferences);
+        return variance;
+    }
+
+    public static <TValue extends Number> double getStandardDeviation(Collection<TValue> collection) {
+        validateCollection(collection);
+
+        double variance = getVariance(collection);
+        return Math.sqrt(variance);
+    }
+
+    /**
+     * Gets the jitter values associated with a set of sensor events.
+     *
+     * @param events The events to use to obtain the jittering information.
+     * @param jitterValues The Collection that will contain the computed jitter values.
+     * @return The mean of the jitter Values.
+     */
+    public static double getJitterMean(
+            TestSensorManager.SensorEventForTest events[],
+            Collection<Double> jitterValues) {
+        ArrayList<Long> timestampDelayValues = new ArrayList<Long>();
+        double averageTimestampDelay = SensorCtsHelper.getAverageTimestampDelayWithValues(events,
+                timestampDelayValues);
+        for(long frequency : timestampDelayValues) {
+            jitterValues.add(Math.abs(averageTimestampDelay - frequency));
+        }
+
+        double jitterMean = SensorCtsHelper.getMean(timestampDelayValues);
+        return jitterMean;
+    }
+
+    /**
+     * Gets the frequency values associated with a set of sensor events.
+     *
+     * @param events The events to use to obtain the frequency information.
+     * @param timestampDelayValues The Collection that will contain the computed frequency values.
+     * @return The mean of the frequency values.
+     */
+    public static double getAverageTimestampDelayWithValues(
+            TestSensorManager.SensorEventForTest events[],
+            Collection<Long> timestampDelayValues) {
+        for(int i = 1; i < events.length; ++i) {
+            long previousTimestamp = events[i-1].timestamp;
+            long timestamp = events[i].timestamp;
+            timestampDelayValues.add(timestamp - previousTimestamp);
+        }
+
+        double timestampDelayMean = SensorCtsHelper.getMean(timestampDelayValues);
+        return timestampDelayMean;
+    }
+
+    public static int getSecondsAsMicroSeconds(int seconds) {
+        return (int) TimeUnit.MICROSECONDS.convert(seconds, TimeUnit.SECONDS);
+    }
+
+    /**
+     * NOTE: The bug report is usually written to /sdcard/Downloads
+     */
+    public static void collectBugreport(String collectorId)
+            throws IOException, InterruptedException {
+        String commands[] = new String[] {
+                "dumpstate",
+                "dumpsys",
+                "logcat -d -v threadtime",
+                "exit"
+        };
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-y_H:m:s.S");
+        String outputFile = String.format(
+                "%s/%s_%s",
+                collectorId,
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+                dateFormat.format(new Date()));
+
+        DataOutputStream processOutput = null;
+        try {
+            Process process = Runtime.getRuntime().exec("/system/bin/sh -");
+            processOutput = new DataOutputStream(process.getOutputStream());
+
+            for(String command : commands) {
+                processOutput.writeBytes(String.format("%s >> %s\n", command, outputFile));
+            }
+
+            processOutput.flush();
+            process.waitFor();
+
+            Log.d(collectorId, String.format("Bug-Report collected at: %s", outputFile));
+        } finally {
+            if(processOutput != null) {
+                try {
+                    processOutput.close();
+                } catch(IOException e) {}
+            }
+        }
+    }
+
+    public static void performOperationInThreads(int numberOfThreadsToUse, Runnable operation)
+            throws InterruptedException {
+        ArrayList<Thread> threads = new ArrayList<Thread>();
+        for(int i = 0; i < numberOfThreadsToUse; ++i) {
+            threads.add(new Thread(operation));
+        }
+
+        while(!threads.isEmpty()) {
+            Thread thread = threads.remove(0);
+            thread.join();
+        }
+    }
+
+    /**
+     * Private helpers
+     */
+    private static void validateCollection(Collection collection) {
+        if(collection == null || collection.size() == 0) {
+            throw new IllegalStateException("Collection cannot be null or empty");
+        }
+    }
+}
+
+
+
+
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
new file mode 100644
index 0000000..9a95e9b
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+
+import java.io.Closeable;
+
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+/**
+ * Test class to wrap SensorManager with verifications and test checks.
+ * This class allows to perform operations in the Sensor Manager and performs all the expected test
+ * verification on behalf of th owner.
+ * An object can be used to quickly writing tests that focus on the scenario that needs to be verified,
+ * and not in the implicit verifications that need to take place at any step.
+ */
+public class TestSensorManager implements Closeable {
+    private final int WAIT_TIMEOUT_IN_SECONDS = 30;
+
+    private Assert mAssert;
+    private SensorManager mSensorManager;
+    private Sensor mSensorUnderTest;
+    private TestSensorListener mEventListener;
+
+    public TestSensorManager(Assert assertionObject, SensorManager sensorManager, Sensor sensor) {
+        mAssert = assertionObject;
+        mSensorManager = sensorManager;
+        mSensorUnderTest = sensor;
+
+        mEventListener = new TestSensorListener();
+    }
+
+    public void close() {
+        this.unregisterListener();
+        mEventListener = null;
+        mSensorUnderTest = null;
+    }
+
+    public SensorManager getUnderlyingSensorManager() {
+        return mSensorManager;
+    }
+
+    public Sensor getSensorUnderTest() {
+        return mSensorUnderTest;
+    }
+
+    public void registerListener(int delay, String debugInfo) {
+        mAssert.assertTrue(
+                "registerListener| " + debugInfo,
+                mSensorManager.registerListener(mEventListener, mSensorUnderTest, delay));
+    }
+
+    public void registerListener(int delay) {
+        registerListener(delay, "");
+    }
+
+    public void registerBatchListener(int delay, int reportLatency, String debugInfo) {
+        boolean result = mSensorManager.registerListener(
+                mEventListener,
+                mSensorUnderTest,
+                delay,
+                reportLatency);
+        mAssert.assertTrue("registerBatchListener| " + debugInfo, result);
+    }
+
+    public void registerBatchListener(int delay, int reportLatency) {
+        registerBatchListener(delay, reportLatency, "");
+    }
+
+    public void unregisterListener() {
+        mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
+    }
+
+    public SensorEventForTest[] getEvents(int count, String debugInfo) {
+        mEventListener.waitForEvents(count, debugInfo);
+        SensorEventForTest[] events = mEventListener.getAllEvents();
+        mEventListener.clearEvents();
+
+        return events;
+    }
+
+    public SensorEventForTest[] getEvents(int count) {
+        return this.getEvents(count, "");
+    }
+
+    public SensorEventForTest[] collectEvents(
+            int collectionDelay,
+            int eventCount,
+            String debugInfo) {
+        this.registerListener(collectionDelay, debugInfo);
+        SensorEventForTest[] events = this.getEvents(eventCount, debugInfo);
+        this.unregisterListener();
+
+        return events;
+    }
+
+    public SensorEventForTest[] collectEvents(int collectionDelay, int eventCount) {
+        return this.collectEvents(collectionDelay, eventCount, "");
+    }
+
+    public SensorEventForTest[] collectBatchEvents(
+            int collectionDelay,
+            int batchReportLatency,
+            int eventCount,
+            String debugInfo) {
+        this.registerBatchListener(collectionDelay, batchReportLatency, debugInfo);
+        SensorEventForTest[] events = this.getEvents(eventCount, debugInfo);
+        this.unregisterListener();
+
+        return events;
+    }
+
+    public SensorEventForTest[] collectBatchEvents(
+            int collectionDelay,
+            int batchReportLatency,
+            int eventCount) {
+        return this.collectBatchEvents(collectionDelay, batchReportLatency, eventCount, "");
+    }
+
+    public void waitForFlush() throws InterruptedException {
+        mAssert.assertTrue(
+                String.format("flush| sensorType:%d", mSensorUnderTest.getType()),
+                mSensorManager.flush(mEventListener));
+        mEventListener.waitForFlushComplete();
+    }
+
+    /**
+     * Definition of support test classes.
+     */
+    public class SensorEventForTest {
+        public final Sensor sensor;
+        public final long timestamp;
+        public final int accuracy;
+        public final float values[];
+
+        public SensorEventForTest(SensorEvent event) {
+            values = new float[event.values.length];
+            System.arraycopy(event.values, 0, values, 0, event.values.length);
+
+            sensor = event.sensor;
+            timestamp = event.timestamp;
+            accuracy = event.accuracy;
+        }
+    }
+
+    private class TestSensorListener implements SensorEventListener2 {
+        private final ConcurrentLinkedDeque<SensorEventForTest> mSensorEventsList =
+                new ConcurrentLinkedDeque<SensorEventForTest>();
+        private volatile CountDownLatch mEventLatch;
+        private volatile CountDownLatch mFlushLatch;
+
+        @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 SensorEventForTest(event));
+                latch.countDown();
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+
+        @Override
+        public void onFlushCompleted(Sensor sensor) {
+            CountDownLatch latch = mFlushLatch;
+            mFlushLatch = new CountDownLatch(1);
+
+            if(latch != null) {
+                latch.countDown();
+            }
+        }
+
+        public void waitForFlushComplete() throws InterruptedException {
+            CountDownLatch latch = mFlushLatch;
+            mAssert.assertTrue(
+                    "WaitForFlush",
+                    latch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
+        }
+
+        public void waitForEvents(int eventCount) {
+            waitForEvents(eventCount, "");
+        }
+
+        public void waitForEvents(int eventCount, String timeoutInfo) {
+            mEventLatch = new CountDownLatch(eventCount);
+            try {
+                boolean awaitCompleted = mEventLatch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+                // TODO: can we collect bug reports on error based only if needed? env var?
+
+                String assertMessage = String.format(
+                        "WaitForEvents| count:%d, available:%d, %s",
+                        eventCount,
+                        mSensorEventsList.size(),
+                        timeoutInfo);
+                mAssert.assertTrue(assertMessage, awaitCompleted);
+            } catch(InterruptedException e) {
+            } finally {
+                mEventLatch = null;
+            }
+        }
+
+        public SensorEventForTest getLastEvent() {
+            return mSensorEventsList.getLast();
+        }
+
+        public SensorEventForTest[] getAllEvents() {
+            return mSensorEventsList.toArray(new SensorEventForTest[0]);
+        }
+
+        public void clearEvents() {
+            mSensorEventsList.clear();
+        }
+    }
+
+    public class TestTriggerListener extends TriggerEventListener {
+        @Override
+        public void onTrigger(TriggerEvent event) {
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
new file mode 100644
index 0000000..4f8fb62
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecList;
+import android.media.MediaPlayer;
+
+import android.util.Log;
+
+/**
+ * Basic sanity test of data returned by MediaCodeCapabilities.
+ */
+public class MediaCodecCapabilitiesTest extends MediaPlayerTestBase {
+
+    private static final String TAG = "MediaCodecCapabilitiesTest";
+    private static final String AVC_MIME = "video/avc";
+    private static final int PLAY_TIME_MS = 30000;
+
+    public void testAvcBaseline1() throws Exception {
+        if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
+                CodecProfileLevel.AVCLevel1)) {
+            throw new RuntimeException("AVCLevel1 support is required by CDD");
+        }
+        // We don't have a test stream, but at least we're testing
+        // that supports() returns true for something.
+    }
+
+    public void testAvcBaseline12() throws Exception {
+        if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
+                CodecProfileLevel.AVCLevel12)) {
+            Log.i(TAG, "AvcBaseline12 not supported");
+            return;  // TODO: Can we make this mandatory?
+        }
+        playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+                + "&itag=160&source=youtube&user=android-device-test"
+                + "&sparams=ip,ipbits,expire,id,itag,source,user"
+                + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+                + "&signature=341692D20FACCAE25B90EA2C131EA6ADCD8E2384."
+                + "9EB08C174BE401AAD20FB85EE4DBA51A2882BB60"
+                + "&key=test_key1", 256, 144, PLAY_TIME_MS);
+    }
+
+    public void testAvcBaseline30() throws Exception {
+        if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
+                CodecProfileLevel.AVCLevel3)) {
+            Log.i(TAG, "AvcBaseline30 not supported");
+            return;
+        }
+        playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+                + "&itag=18&source=youtube&user=android-device-test"
+                + "&sparams=ip,ipbits,expire,id,itag,source,user"
+                + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+                + "&signature=8701A45F6422229D46ABB25A22E2C00C94024606."
+                + "08BCDF16C3F744C49D4C8A8AD1C38B3DC1810918"
+                + "&key=test_key1", 640, 360, PLAY_TIME_MS);
+    }
+
+    public void testAvcHigh31() throws Exception {
+        if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh,
+                CodecProfileLevel.AVCLevel31)) {
+            Log.i(TAG, "AvcHigh31 not supported");
+            return;
+        }
+        playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+                + "&itag=22&source=youtube&user=android-device-test"
+                + "&sparams=ip,ipbits,expire,id,itag,source,user"
+                + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+                + "&signature=42969CA8F7FFAE432B7135BC811F96F7C4172C3F."
+                + "1A8A92EA714C1B7C98A05DDF2DE90854CDD7638B"
+                + "&key=test_key1", 1280, 720, PLAY_TIME_MS);
+
+    }
+
+    public void testAvcHigh40() throws Exception {
+        if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh,
+                CodecProfileLevel.AVCLevel4)) {
+            Log.i(TAG, "AvcHigh40 not supported");
+            return;
+        }
+        playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+                + "&itag=37&source=youtube&user=android-device-test"
+                + "&sparams=ip,ipbits,expire,id,itag,source,user"
+                + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+                + "&signature=7C3BBFB2F493E1BC396B6D31DDAF2E1367624487."
+                + "64197F3BB46039669E912297DCD68D1FB2811D9F"
+                + "&key=test_key1", 1920, 1080, PLAY_TIME_MS);
+    }
+
+    private boolean supports(String mimeType, int profile, int level) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+            if (codecInfo.isEncoder()) {
+                continue;
+            }
+
+            CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
+            for (CodecProfileLevel profileLevel : capabilities.profileLevels) {
+                if (profileLevel.profile == profile
+                        && profileLevel.level >= level) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/tools/device-setup/TestDeviceSetup/Android.mk b/tools/device-setup/TestDeviceSetup/Android.mk
index 39f989e..ba1998c 100644
--- a/tools/device-setup/TestDeviceSetup/Android.mk
+++ b/tools/device-setup/TestDeviceSetup/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_SDK_VERSION := 17
+LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := TestDeviceSetup
 
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoActivity.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoActivity.java
index 3614e22..e18e626 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoActivity.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoActivity.java
@@ -23,10 +23,10 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 
+import java.util.HashSet;
 import java.util.Locale;
 import java.util.concurrent.CountDownLatch;
 
-
 /**
  * Collect device information on target device.
  */
@@ -34,7 +34,7 @@
 
     // work done should be reported in GLES..View
     private CountDownLatch mDone = new CountDownLatch(1);
-    private GLESSurfaceView mGLView;
+    private HashSet<String> mFormats = new HashSet<String>();
 
     /**
      * Other classes can call this function to wait for this activity
@@ -47,6 +47,37 @@
         }
     }
 
+    private void runIterations(int glVersion) {
+        for (int i = 1; i <= glVersion; i++) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final int version = i;
+            DeviceInfoActivity.this.runOnUiThread(new Runnable() {
+              public void run() {
+                setContentView(new GLESSurfaceView(DeviceInfoActivity.this, version, done));
+              }
+            });
+            try {
+                done.await();
+            } catch (InterruptedException e) {
+                // just move on
+            }
+        }
+
+        StringBuilder builder = new StringBuilder();
+        for (String format: mFormats) {
+            builder.append(format);
+            builder.append(";");
+        }
+        DeviceInfoInstrument.addResult(
+                DeviceInfoConstants.OPEN_GL_COMPRESSED_TEXTURE_FORMATS,
+                builder.toString());
+        mDone.countDown();
+    }
+
+    public void addCompressedTextureFormat(String format) {
+        mFormats.add(format);
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -54,10 +85,12 @@
         ActivityManager am =
                 (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
         ConfigurationInfo info = am.getDeviceConfigurationInfo();
-        boolean useGL20 = (info.reqGlEsVersion >= 0x20000);
-
-        mGLView = new GLESSurfaceView(this, useGL20, mDone);
-        setContentView(mGLView);
+        final int glVersion = (info.reqGlEsVersion & 0xffff0000) >> 16;
+        new Thread() {
+          public void run() {
+            runIterations(glVersion);
+          }
+        }.start();
 
         Configuration con = getResources().getConfiguration();
         String touchScreen = null;
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
index c06c21e..4a0ea66 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
@@ -60,4 +60,6 @@
     public static final String BUILD_VERSION = "buildVersion";
     public static final String BUILD_TAGS = "build_tags";
     public static final String SERIAL_NUMBER = "deviceID";
+    public static final String STORAGE_DEVICES = "storage_devices";
+    public static final String MULTI_USER = "multi_user";
 }
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
index 49e96a0..29b29bf 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
@@ -25,6 +25,8 @@
 import android.content.res.Configuration;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
+import android.os.UserManager;
 import android.telephony.TelephonyManager;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -33,6 +35,8 @@
 
 import java.io.IOException;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -135,6 +139,12 @@
         String sysLibraries = getSystemLibraries();
         addResult(SYS_LIBRARIES, sysLibraries);
 
+        // Storage devices
+        addResult(STORAGE_DEVICES, getStorageDevices());
+
+        // Multi-user support
+        addResult(MULTI_USER, getMultiUserInfo());
+
         finish(Activity.RESULT_OK, mResults);
     }
 
@@ -343,4 +353,42 @@
 
         return builder.toString();
     }
+
+    private String getStorageDevices() {
+        int count = 0;
+        count = Math.max(count, getContext().getExternalCacheDirs().length);
+        count = Math.max(count, getContext().getExternalFilesDirs(null).length);
+        count = Math.max(
+                count, getContext().getExternalFilesDirs(Environment.DIRECTORY_PICTURES).length);
+        count = Math.max(count, getContext().getObbDirs().length);
+
+        final String result;
+        if (Environment.isExternalStorageEmulated()) {
+            if (count == 1) {
+                return "1 emulated";
+            } else {
+                return "1 emulated, " + (count - 1) + " physical media";
+            }
+        } else {
+            return count + " physical media";
+        }
+    }
+
+    private String getMultiUserInfo() {
+        try {
+            final Method method = UserManager.class.getMethod("getMaxSupportedUsers");
+            final Integer maxUsers = (Integer) method.invoke(null);
+            if (maxUsers == 1) {
+                return "single user";
+            } else {
+                return maxUsers + " users supported";
+            }
+        } catch (ClassCastException e) {
+        } catch (NoSuchMethodException e) {
+        } catch (InvocationTargetException e) {
+        } catch (IllegalAccessException e) {
+        }
+
+        return "unknown";
+    }
 }
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/GLESSurfaceView.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/GLESSurfaceView.java
index 0ec11b7..b2f2211 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/GLESSurfaceView.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/GLESSurfaceView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.opengl.GLES20;
+import android.opengl.GLES30;
 import android.opengl.GLSurfaceView;
 import android.util.Log;
 
@@ -30,22 +31,24 @@
 class GLESSurfaceView extends GLSurfaceView {
     private static final String TAG = "GLESSurfaceView";
 
-    private boolean mUseGL20;
-    CountDownLatch mDone;
-
+    private int mGLVersion;//1, 2, 3
+    private CountDownLatch mDone;
+    private DeviceInfoActivity mParent;
     /**
      *
-     * @param context
+     * @param parent
      * @param useGL20 whether to use GLES2.0 API or not inside the view
      * @param done to notify the completion of the task
      */
-    public GLESSurfaceView(Context context, boolean useGL20, CountDownLatch done){
-        super(context);
+    public GLESSurfaceView(DeviceInfoActivity parent, int glVersion, CountDownLatch done){
+        super(parent);
 
-        mUseGL20 = useGL20;
+        mParent = parent;
+        mGLVersion = glVersion;
         mDone = done;
-        if (mUseGL20) {
-            setEGLContextClientVersion(2);
+        if (glVersion > 1) {
+            // Default is 1 so only set if bigger than 1
+            setEGLContextClientVersion(glVersion);
         }
         setRenderer(new OpenGLESRenderer());
     }
@@ -55,30 +58,26 @@
         @Override
         public void onSurfaceCreated(GL10 gl, EGLConfig config) {
             String extensions;
-            if (mUseGL20) {
+            if (mGLVersion == 2) {
                 extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS);
+            } else if (mGLVersion == 3) {
+                extensions = GLES30.glGetString(GLES30.GL_EXTENSIONS);
             } else {
                 extensions = gl.glGetString(GL10.GL_EXTENSIONS);
             }
             Log.i(TAG, "extensions : " + extensions);
             Scanner scanner = new Scanner(extensions);
             scanner.useDelimiter(" ");
-            StringBuilder builder = new StringBuilder();
             while (scanner.hasNext()) {
                 String ext = scanner.next();
                 if (ext.contains("texture")) {
                     if (ext.contains("compression") || ext.contains("compressed")) {
                         Log.i(TAG, "Compression supported: " + ext);
-                        builder.append(ext);
-                        builder.append(";");
+                        mParent.addCompressedTextureFormat(ext);
                     }
                 }
             }
 
-            DeviceInfoInstrument.addResult(
-                    DeviceInfoConstants.OPEN_GL_COMPRESSED_TEXTURE_FORMATS,
-                    builder.toString());
-
             mDone.countDown();
         }
 
@@ -93,4 +92,4 @@
         }
 
     }
-}
\ No newline at end of file
+}
diff --git a/tools/tradefed-host/res/report/cts_result.xsd b/tools/tradefed-host/res/report/cts_result.xsd
index 3f7f384..ce606e9 100644
--- a/tools/tradefed-host/res/report/cts_result.xsd
+++ b/tools/tradefed-host/res/report/cts_result.xsd
@@ -78,6 +78,8 @@
         <xs:attribute name="partitions" type="xs:string"/>
         <xs:attribute name="build_abi" type="xs:string"/>
         <xs:attribute name="build_abi2" type="xs:string"/>
+        <xs:attribute name="storage_devices" type="xs:string"/>
+        <xs:attribute name="multi_user" type="xs:string"/>
       </xs:complexType>
     </xs:element>
     <xs:element name="FeatureInfo" type="featureInfoType"/>
diff --git a/tools/tradefed-host/res/report/cts_result.xsl b/tools/tradefed-host/res/report/cts_result.xsl
index dfddc09..b6fb699 100644
--- a/tools/tradefed-host/res/report/cts_result.xsl
+++ b/tools/tradefed-host/res/report/cts_result.xsl
@@ -298,6 +298,18 @@
                                             </pre>
                                         </TD>
                                     </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Storage devices</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@storage_devices"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Multi-user support</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@multi_user"/>
+                                        </TD>
+                                    </TR>
                                 </TABLE>
                             </TD>
                         </TR>