am f162ed19: Merge "Excluded irrelevant tests from clockwork devices." into lmp-sprout-dev

* commit 'f162ed1921455db3d91d70fb2f98dea20adea909':
  Excluded irrelevant tests from clockwork devices.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index b4863fa..3d0e0d7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -31,9 +31,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 public class MockListener extends NotificationListenerService {
     static final String TAG = "MockListener";
@@ -69,10 +67,8 @@
 
     private ArrayList<String> mPosted = new ArrayList<String>();
     private ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
-    private ArrayMap<String, String> mNotificationKeys = new ArrayMap<>();
     private ArrayList<String> mRemoved = new ArrayList<String>();
     private ArrayList<String> mOrder = new ArrayList<>();
-    private Set<String> mTestPackages = new HashSet<>();
     private BroadcastReceiver mReceiver;
     private int mDND = -1;
 
@@ -81,8 +77,6 @@
         super.onCreate();
         Log.d(TAG, "created");
 
-        mTestPackages.add("com.android.cts.verifier");
-
         mPosted = new ArrayList<String>();
         mRemoved = new ArrayList<String>();
 
@@ -129,13 +123,10 @@
                     setResultCode(Activity.RESULT_OK);
                 } else if (SERVICE_CLEAR_ONE.equals(action)) {
                     Log.d(TAG, "SERVICE_CLEAR_ONE");
-                    String tag = intent.getStringExtra(EXTRA_TAG);
-                    String key = mNotificationKeys.get(tag);
-                    if (key != null) {
-                        MockListener.this.cancelNotification(key);
-                    } else {
-                        Log.w(TAG, "Notification does not exist: " + tag);
-                    }
+                    MockListener.this.cancelNotification(
+                            context.getApplicationInfo().packageName,
+                            intent.getStringExtra(EXTRA_TAG),
+                            intent.getIntExtra(EXTRA_CODE, 0));
                 } else if (SERVICE_CLEAR_ALL.equals(action)) {
                     Log.d(TAG, "SERVICE_CLEAR_ALL");
                     MockListener.this.cancelAllNotifications();
@@ -214,7 +205,6 @@
 
     @Override
     public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
-        if (!mTestPackages.contains(sbn.getPackageName())) { return; }
         Log.d(TAG, "posted: " + sbn.getTag());
         mPosted.add(sbn.getTag());
         JSONObject notification = new JSONObject();
@@ -226,7 +216,6 @@
             notification.put(JSON_ICON, sbn.getNotification().icon);
             notification.put(JSON_FLAGS, sbn.getNotification().flags);
             mNotifications.put(sbn.getKey(), notification);
-            mNotificationKeys.put(sbn.getTag(), sbn.getKey());
         } catch (JSONException e) {
             Log.e(TAG, "failed to pack up notification payload", e);
         }
@@ -239,7 +228,6 @@
         mRemoved.add(sbn.getTag());
         mNotifications.remove(sbn.getKey());
         onNotificationRankingUpdate(rankingMap);
-        mNotificationKeys.remove(sbn.getTag());
     }
 
     public static void resetListenerData(Context context) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index dfcf120..4a0c135 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -97,7 +97,7 @@
                 Sensor.TYPE_ACCELEROMETER,
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation verifyMeasurements =
-                new TestSensorOperation(environment, 100 /* event count */);
+                TestSensorOperation.createOperation(environment, 100 /* event count */);
         verifyMeasurements.addVerification(new MeanVerification(
                 expectations,
                 new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
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 6f0a7aa..2d58c64 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -22,9 +22,7 @@
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
 import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
 
 import java.util.concurrent.TimeUnit;
 
@@ -128,7 +126,7 @@
 
         int testDurationSec = maxBatchReportLatencySec + BATCHING_PADDING_TIME_S;
         TestSensorOperation operation =
-                new TestSensorOperation(environment, testDurationSec,TimeUnit.SECONDS);
+                TestSensorOperation.createOperation(environment, testDurationSec,TimeUnit.SECONDS);
         return executeTest(operation);
     }
 
@@ -145,12 +143,12 @@
                 maxBatchReportLatencyUs);
 
         int flushDurationSec = maxBatchReportLatencySec / 2;
-        TestSensorFlushOperation operation =
-                new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+        TestSensorOperation operation = TestSensorOperation
+                .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
         return executeTest(operation);
     }
 
-    private String executeTest(VerifiableSensorOperation operation) throws InterruptedException {
+    private String executeTest(TestSensorOperation operation) throws InterruptedException {
         operation.addDefaultVerifications();
         operation.setLogEvents(true);
         operation.execute();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index 4b2a7f4..d6ec951 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -156,8 +156,8 @@
                 getApplicationContext(),
                 Sensor.TYPE_GYROSCOPE,
                 SensorManager.SENSOR_DELAY_FASTEST);
-        TestSensorOperation sensorOperation =
-                new TestSensorOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
+        TestSensorOperation sensorOperation = TestSensorOperation
+                .createOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
 
         int gyroscopeAxes = environment.getSensorAxesCount();
         int[] expectationsDeg = getExpectationsDeg(gyroscopeAxes, rotationAxis, expectationDeg);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index 553147b..1ec5dc1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -79,7 +79,7 @@
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation verifyNorm =
-                new TestSensorOperation(environment, 100 /* event count */);
+                TestSensorOperation.createOperation(environment, 100 /* event count */);
 
         float expectedMagneticFieldEarth =
                 (SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
@@ -124,7 +124,7 @@
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation verifyStdDev =
-                new TestSensorOperation(environment, 100 /* event count */);
+                TestSensorOperation.createOperation(environment, 100 /* event count */);
 
         verifyStdDev.addVerification(new StandardDeviationVerification(
                 new float[]{2f, 2f, 2f} /* uT */));
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 35a0b4c..306428f 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -16,8 +16,11 @@
 package android.animation.cts;
 
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -25,6 +28,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
+import android.view.animation.LinearInterpolator;
 
 public class AnimatorSetTest extends
         ActivityInstrumentationTestCase2<AnimationActivity> {
@@ -34,6 +38,7 @@
     private Object object;
     private ObjectAnimator yAnimator;
     private ObjectAnimator xAnimator;
+    Set<Integer> identityHashes = new HashSet<Integer>();
 
     public AnimatorSetTest() {
         super(AnimationActivity.class);
@@ -158,4 +163,79 @@
             }
         });
     }
+
+    private void assertUnique(Object object) {
+        assertUnique(object, "");
+    }
+
+    private void assertUnique(Object object, String msg) {
+        final int code = System.identityHashCode(object);
+        assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
+
+    }
+
+    public void testClone() {
+        AnimatorSet set1 = new AnimatorSet();
+        final AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {};
+        set1.addListener(setListener);
+        ObjectAnimator animator1 = new ObjectAnimator();
+        animator1.setDuration(100);
+        animator1.setPropertyName("x");
+        animator1.setIntValues(5);
+        animator1.setInterpolator(new LinearInterpolator());
+        AnimatorListenerAdapter listener1 = new AnimatorListenerAdapter(){};
+        AnimatorListenerAdapter listener2 = new AnimatorListenerAdapter(){};
+        animator1.addListener(listener1);
+
+        ObjectAnimator animator2 = new ObjectAnimator();
+        animator2.setDuration(100);
+        animator2.setInterpolator(new LinearInterpolator());
+        animator2.addListener(listener2);
+        animator2.setPropertyName("y");
+        animator2.setIntValues(10);
+
+        set1.playTogether(animator1, animator2);
+
+        AnimateObject target = new AnimateObject();
+        set1.setTarget(target);
+        set1.start();
+        assertTrue(set1.isStarted());
+
+        animator1.getListeners();
+        AnimatorSet set2 = set1.clone();
+        assertFalse(set2.isStarted());
+
+        assertUnique(set1);
+        assertUnique(animator1);
+        assertUnique(animator2);
+
+        assertUnique(set2);
+        assertEquals(2, set2.getChildAnimations().size());
+
+        Animator clone1 = set2.getChildAnimations().get(0);
+        Animator clone2 = set2.getChildAnimations().get(1);
+
+        for (Animator animator : set2.getChildAnimations()) {
+            assertUnique(animator);
+        }
+
+        assertTrue(clone1.getListeners().contains(listener1));
+        assertTrue(clone2.getListeners().contains(listener2));
+
+        assertTrue(set2.getListeners().contains(setListener));
+
+        for (Animator.AnimatorListener listener : set1.getListeners()) {
+            assertTrue(set2.getListeners().contains(listener));
+        }
+
+        assertEquals(animator1.getDuration(), clone1.getDuration());
+        assertEquals(animator2.getDuration(), clone2.getDuration());
+        assertSame(animator1.getInterpolator(), clone1.getInterpolator());
+        assertSame(animator2.getInterpolator(), clone2.getInterpolator());
+    }
+
+    class AnimateObject {
+        int x = 1;
+        int y = 2;
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
index 687826c..bd47024 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
@@ -20,9 +20,7 @@
 import android.hardware.SensorManager;
 import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.TestSensorEnvironment;
-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;
@@ -263,7 +261,7 @@
                 rateUs,
                 maxBatchReportLatencyUs);
         TestSensorOperation operation =
-                new TestSensorOperation(environment, testDurationSec, TimeUnit.SECONDS);
+                TestSensorOperation.createOperation(environment, testDurationSec, TimeUnit.SECONDS);
 
         executeTest(environment, operation, false /* flushExpected */);
     }
@@ -279,15 +277,15 @@
                 shouldEmulateSensorUnderLoad(),
                 rateUs,
                 maxBatchReportLatencyUs);
-        TestSensorFlushOperation operation =
-                new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+        TestSensorOperation operation = TestSensorOperation
+                .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
 
         executeTest(environment, operation, true /* flushExpected */);
     }
 
     private void executeTest(
             TestSensorEnvironment environment,
-            VerifiableSensorOperation operation,
+            TestSensorOperation operation,
             boolean flushExpected) throws Throwable {
         operation.addDefaultVerifications();
         operation.setLogEvents(true);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index 50cb12d..4dfa16e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -24,7 +24,6 @@
 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 java.util.Random;
@@ -81,7 +80,7 @@
                     shouldEmulateSensorUnderLoad(),
                     SensorManager.SENSOR_DELAY_FASTEST);
             TestSensorOperation continuousOperation =
-                    new TestSensorOperation(environment, 100 /* eventCount */);
+                    TestSensorOperation.createOperation(environment, 100 /* eventCount */);
             continuousOperation.addVerification(new EventOrderingVerification());
             operation.add(new RepeatingSensorOperation(continuousOperation, ITERATIONS));
 
@@ -93,7 +92,7 @@
                     sensor.getMinDelay(),
                     MAX_REPORTING_LATENCY_US);
             TestSensorOperation batchingOperation =
-                    new TestSensorOperation(batchingEnvironment, 100 /* eventCount */);
+                    TestSensorOperation.createOperation(batchingEnvironment, 100 /* eventCount */);
             batchingOperation.addVerification(new EventOrderingVerification());
             operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
         }
@@ -145,7 +144,7 @@
                             generateSamplingRateInUs(sensorType),
                             generateReportLatencyInUs());
                     TestSensorOperation sensorOperation =
-                            new TestSensorOperation(environment, 100 /* eventCount */);
+                            TestSensorOperation.createOperation(environment, 100 /* eventCount */);
                     sensorOperation.addVerification(new EventOrderingVerification());
                     sequentialOperation.add(sensorOperation);
                 }
@@ -229,7 +228,7 @@
                 shouldEmulateSensorUnderLoad(),
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation tester =
-                new TestSensorOperation(testerEnvironment, 100 /* event count */);
+                TestSensorOperation.createOperation(testerEnvironment, 100 /* event count */);
         tester.addVerification(new EventOrderingVerification());
 
         TestSensorEnvironment testeeEnvironment = new TestSensorEnvironment(
@@ -237,8 +236,8 @@
                 sensorTypeTestee,
                 shouldEmulateSensorUnderLoad(),
                 SensorManager.SENSOR_DELAY_FASTEST);
-        VerifiableSensorOperation testee =
-                new TestSensorOperation(testeeEnvironment, 100 /* event count */);
+        TestSensorOperation testee =
+                TestSensorOperation.createOperation(testeeEnvironment, 100 /* event count */);
         testee.addVerification(new EventOrderingVerification());
 
         ParallelSensorOperation operation = new ParallelSensorOperation();
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index d8b8e51..08d06c6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -18,6 +18,8 @@
 
 import com.android.cts.util.TimeoutReq;
 
+import junit.framework.Assert;
+
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.Sensor;
@@ -27,44 +29,70 @@
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class SensorTest extends SensorTestCase {
-    private SensorManager mSensorManager;
-    private TriggerListener mTriggerListener;
-    private SensorListener mSensorListener;
-    private List<Sensor> mSensorList;
     private static final String TAG = "SensorTest";
+
     // Test only SDK defined sensors. Any sensors with type > 100 are ignored.
     private static final int MAX_OFFICIAL_ANDROID_SENSOR_TYPE = 100;
-    private static final long TIMEOUT_TOLERANCE_US = TimeUnit.SECONDS.toMicros(5);
-    private static final double MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE = 0.9;
+
     private PowerManager.WakeLock mWakeLock;
+    private SensorManager mSensorManager;
+    private NullTriggerEventListener mNullTriggerEventListener;
+    private NullSensorEventListener mNullSensorEventListener;
+    private List<Sensor> mSensorList;
 
     @Override
     protected void setUp() throws Exception {
-        super.setUp();
-        mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
-        mTriggerListener = new TriggerListener();
-        mSensorListener = new SensorListener();
-        mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
-        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+        Context context = getContext();
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        mNullTriggerEventListener = new NullTriggerEventListener();
+        mNullSensorEventListener = new NullSensorEventListener();
+
+        mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        assertNotNull("SensorList was null.", mSensorList);
+        if (mSensorList.isEmpty()) {
+            // several devices will not have sensors, so we need to skip the tests in those cases
+            throw new SensorTestStateNotSupportedException(
+                    "Sensors are not available in the system.");
+        }
+
+        mWakeLock.acquire();
     }
 
+    @Override
+    protected void tearDown(){
+        if (mWakeLock != null && mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
+    }
+
+    @SuppressWarnings("deprecation")
     public void testSensorOperations() {
         // Because we can't know every sensors unit details, so we can't assert
         // get values with specified values.
-        List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
-        assertNotNull(sensors);
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
         boolean hasAccelerometer = getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SENSOR_ACCELEROMETER);
@@ -149,7 +177,6 @@
         }
         assertTrue(sensors.get(0).getName() + " defined as non-wake-up sensor",
                 sensors.get(0).isWakeUpSensor());
-        return;
     }
 
     // Some sensors like proximity, significant motion etc. are defined as wake-up sensors by
@@ -207,372 +234,137 @@
 
     public void testRequestTriggerWithNonTriggerSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.requestTriggerSensor(mTriggerListener, sensor);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
+        boolean  result = mSensorManager.requestTriggerSensor(mNullTriggerEventListener, sensor);
+        assertFalse(result);
     }
 
     public void testCancelTriggerWithNonTriggerSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.cancelTriggerSensor(mTriggerListener, sensor);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
+        boolean result = mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, sensor);
+        assertFalse(result);
     }
 
     public void testRegisterWithTriggerSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.registerListener(mSensorListener, sensor,
-                    SensorManager.SENSOR_DELAY_NORMAL);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
         }
+        boolean result = mSensorManager.registerListener(
+                mNullSensorEventListener,
+                sensor,
+                SensorManager.SENSOR_DELAY_NORMAL);
+        assertFalse(result);
     }
 
     public void testRegisterTwiceWithSameSensor() {
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        boolean result;
-        if (sensor != null) {
-            result = mSensorManager.registerListener(mSensorListener, sensor,
-                    SensorManager.SENSOR_DELAY_NORMAL);
-            assertTrue(result);
-            result = mSensorManager.registerListener(mSensorListener, sensor,
-                    SensorManager.SENSOR_DELAY_NORMAL);
-            assertFalse(result);
+        if (sensor == null) {
+            throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
         }
+
+        boolean result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+                SensorManager.SENSOR_DELAY_NORMAL);
+        assertTrue(result);
+
+        result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+                SensorManager.SENSOR_DELAY_NORMAL);
+        assertFalse(result);
     }
 
-    class SensorEventTimeStampListener implements SensorEventListener {
-        SensorEventTimeStampListener(long eventReportLatencyNs, CountDownLatch latch) {
-            mEventReportLatencyNs = eventReportLatencyNs;
-            mPrevTimeStampNs = -1;
-            mLatch = latch;
-            numErrors = 0;
-        }
-
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            if (mPrevTimeStampNs == -1) {
-                mPrevTimeStampNs = event.timestamp;
-                return;
-            }
-            long currTimeStampNs = event.timestamp;
-            if (currTimeStampNs <= mPrevTimeStampNs) {
-                Log.w(TAG, "Timestamps not monotonically increasing curr_ts_ns=" +
-                        event.timestamp + " prev_ts_ns=" + mPrevTimeStampNs);
-                numErrors++;
-                mPrevTimeStampNs = currTimeStampNs;
-                return;
-            }
-            mLatch.countDown();
-
-            final long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
-
-            if (elapsedRealtimeNs <= currTimeStampNs) {
-                Log.w(TAG, "Timestamps into the future curr elapsedRealTimeNs=" + elapsedRealtimeNs
-                         + " current sensor ts_ns=" + currTimeStampNs);
-                ++numErrors;
-            } else if (elapsedRealtimeNs-currTimeStampNs > SYNC_TOLERANCE + mEventReportLatencyNs) {
-                Log.w(TAG, "Timestamp sync error elapsedRealTimeNs=" + elapsedRealtimeNs +
-                        " curr_ts_ns=" + currTimeStampNs +
-                        " diff_ns=" + (elapsedRealtimeNs - currTimeStampNs) +
-                        " SYNC_TOLERANCE_NS=" + SYNC_TOLERANCE +
-                        " eventReportLatencyNs=" + mEventReportLatencyNs);
-                ++numErrors;
-            }
-            mPrevTimeStampNs = currTimeStampNs;
-        }
-
-        public int getNumErrors() {
-            return numErrors;
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-        }
-
-        private int numErrors;
-        private long mEventReportLatencyNs;
-        private long mPrevTimeStampNs;
-        private final CountDownLatch mLatch;
-        private final long SYNC_TOLERANCE = 500000000L; // 500 milli seconds approx.
-    }
-
-    // Register for each sensor and compare the timestamps of SensorEvents that you get with
-    // elapsedRealTimeNano.
+    // TODO: remove when parametized tests are supported and EventTimestampSynchronization
+    //       verification is added to default verifications
     @TimeoutReq(minutes=60)
     public void testSensorTimeStamps() throws Exception {
-        final int numEvents = 2000;
-        try {
-            mWakeLock.acquire();
-            int numErrors = 0;
-            for (Sensor sensor : mSensorList) {
-                // Skip OEM defined sensors and non continuous sensors.
-                if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
-                    continue;
-                }
-
-                for (int iterations = 0; iterations < 2; ++iterations) {
-                    // Test in both batch mode and non-batch mode for every sensor.
-                    long maxBatchReportLatencyNs = 10000000000L; // 10 secs
-                    if (iterations % 2 == 0) maxBatchReportLatencyNs = 0;
-
-                    final long samplingPeriodNs = (long)(TimeUnit.MICROSECONDS.toNanos(
-                            sensor.getMinDelay())/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE);
-                    // If there is a FIFO and a wake-lock is held, events will be reported when
-                    // the batch timeout expires or when the FIFO is full which ever occurs
-                    // earlier.
-                    final long eventReportLatencyNs = Math.min(maxBatchReportLatencyNs,
-                            sensor.getFifoMaxEventCount() * samplingPeriodNs);
-
-                    final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
-                    SensorEventTimeStampListener listener = new SensorEventTimeStampListener(
-                            eventReportLatencyNs, eventsRemaining);
-
-                    Log.i(TAG, "Running timeStamp test on " + sensor.getName());
-                    boolean result = mSensorManager.registerListener(listener, sensor,
-                            SensorManager.SENSOR_DELAY_FASTEST,
-                            (int)maxBatchReportLatencyNs/1000);
-                    assertTrue("Sensor registerListener failed ", result);
-
-                    long timeToWaitUs = samplingPeriodNs/1000 + eventReportLatencyNs/1000 +
-                            TIMEOUT_TOLERANCE_US;
-                    long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
-                            (int)(eventReportLatencyNs/1000), eventsRemaining);
-
-                    mSensorManager.unregisterListener(listener);
-                    if (eventsRemaining.getCount() > 0) {
-                        failTimedOut(sensor.getName(), (double) totalTimeWaitedUs/1000,
-                                numEvents, (double) sensor.getMinDelay()/1000,
-                                eventsRemaining.getCount(),
-                                numEvents - eventsRemaining.getCount());
-                    }
-                    if (listener.getNumErrors() > 5) {
-                        fail("Check logcat. Timestamp test failed. numErrors=" +
-                                listener.getNumErrors() + " " + sensor.getName() +
-                                " maxBatchReportLatencyNs=" + maxBatchReportLatencyNs +
-                                " samplingPeriodNs=" + sensor.getMinDelay());
-                        numErrors += listener.getNumErrors();
-                    } else {
-                        Log.i(TAG, "TimeStamp test PASS'd on " + sensor.getName());
-                    }
-                }
-            }
-        } finally {
-            mWakeLock.release();
+        ArrayList<Throwable> errorsFound = new ArrayList<>();
+        for (Sensor sensor : mSensorList) {
+            // test both continuous and batching mode sensors
+            verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, errorsFound);
+            verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), errorsFound);
         }
+        assertOnErrors(errorsFound);
     }
 
-    private void failTimedOut(String sensorName, double totalTimeWaitedMs, int numEvents,
-            double minDelayMs, long eventsRemaining, long eventsReceived) {
-        final String TIMED_OUT_FORMAT = "Timed out waiting for events from %s " +
-                "waited for time=%.1fms to receive totalEvents=%d at samplingRate=%.1fHz" +
-                " remainingEvents=%d  received events=%d";
-        fail(String.format(TIMED_OUT_FORMAT, sensorName, totalTimeWaitedMs, numEvents,
-                1000/minDelayMs, eventsRemaining, eventsReceived));
-    }
-
-    // Register for updates from each continuous mode sensor, wait for N events, call flush and
-    // wait for flushCompleteEvent before unregistering for the sensor.
+    // TODO: remove when parameterized tests are supported (see SensorBatchingTests.java)
     @TimeoutReq(minutes=20)
     public void testBatchAndFlush() throws Exception {
-        try {
-            mWakeLock.acquire();
-            for (Sensor sensor : mSensorList) {
-                // Skip ONLY one-shot sensors.
-                if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
-                    registerListenerCallFlush(sensor, null);
-                }
-            }
-        } finally {
-            mWakeLock.release();
+        ArrayList<Throwable> errorsFound = new ArrayList<>();
+        for (Sensor sensor : mSensorList) {
+            verifyRegisterListenerCallFlush(sensor, null /* handler */, errorsFound);
         }
+        assertOnErrors(errorsFound);
     }
 
-    // Same as testBatchAndFlush but using Handler version of the API to register for sensors.
-    // onSensorChanged is now called on a background thread.
+    /**
+     * Verifies that sensor events arrive in the given message queue (Handler).
+     */
     @TimeoutReq(minutes=10)
     public void testBatchAndFlushWithHandler() throws Exception {
-        try {
-            mWakeLock.acquire();
-            HandlerThread handlerThread = new HandlerThread("sensorThread");
-            handlerThread.start();
-            Handler handler = new Handler(handlerThread.getLooper());
-            for (Sensor sensor : mSensorList) {
-                // Skip ONLY one-shot sensors.
-                if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
-                    registerListenerCallFlush(sensor, handler);
-                }
+        Sensor sensor = null;
+        for (Sensor s : mSensorList) {
+            if (s.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+                sensor = s;
+                break;
             }
-        }  finally {
-            mWakeLock.release();
         }
+        if (sensor == null) {
+            throw new SensorTestStateNotSupportedException(
+                    "There are no Continuous sensors in the device.");
+        }
+
+        TestSensorEnvironment environment = new TestSensorEnvironment(
+                getContext(),
+                sensor,
+                SensorManager.SENSOR_DELAY_FASTEST,
+                (int) TimeUnit.SECONDS.toMicros(5));
+        TestSensorManager sensorManager = new TestSensorManager(environment);
+
+        HandlerThread handlerThread = new HandlerThread("sensorThread");
+        handlerThread.start();
+        Handler handler = new Handler(handlerThread.getLooper());
+        TestSensorEventListener listener = new TestSensorEventListener(handler);
+
+        sensorManager.registerListener(listener);
+        listener.waitForEvents(1);
+        sensorManager.requestFlush();
+        listener.waitForFlushComplete();
+        listener.assertEventsReceivedInHandler();
     }
 
-    private void registerListenerCallFlush(Sensor sensor, Handler handler)
-            throws InterruptedException {
-        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
-            return;
-        }
-        final int numEvents = 500;
-        final int rateUs = 0; // DELAY_FASTEST
-        final int maxBatchReportLatencyUs = 10000000;
-        final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
-        final CountDownLatch flushReceived = new CountDownLatch(1);
-        SensorEventListener2 listener = new SensorEventListener2() {
-            @Override
-            public void onSensorChanged(SensorEvent event) {
-                eventsRemaining.countDown();
-            }
-
-            @Override
-            public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            }
-
-            @Override
-            public void onFlushCompleted(Sensor sensor) {
-                flushReceived.countDown();
-            }
-        };
-        // Consider only continuous mode sensors for testing registerListener.
-        // For on-change sensors, call registerListener() so that the listener is associated
-        // with the sensor so that flush(listener) can be called on it.
-        try {
-            Log.i(TAG, "testBatch " + sensor.getName());
-            boolean result = mSensorManager.registerListener(listener, sensor,
-                    rateUs, maxBatchReportLatencyUs, handler);
-            assertTrue("registerListener failed " + sensor.getName(), result);
-
-            // Wait for 500 events or N seconds before the test times out.
-            if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
-                // Wait for approximately the time required to generate these events + a tolerance
-                // of 10 seconds.
-                long timeToWaitUs =
-                  numEvents*(long)(sensor.getMinDelay()/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE)
-                        + maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
-
-                long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
-                        maxBatchReportLatencyUs, eventsRemaining);
-                if (eventsRemaining.getCount() > 0) {
-                    failTimedOut(sensor.getName(), (double)totalTimeWaitedUs/1000, numEvents,
-                            (double)sensor.getMinDelay()/1000,
-                            eventsRemaining.getCount(), numEvents - eventsRemaining.getCount());
-                }
-            }
-            Log.i(TAG, "testFlush " + sensor.getName());
-            result = mSensorManager.flush(listener);
-            assertTrue("flush failed " + sensor.getName(), result);
-            boolean collectedAllEvents = flushReceived.await(TIMEOUT_TOLERANCE_US,
-                    TimeUnit.MICROSECONDS);
-            if (!collectedAllEvents) {
-                fail("Timed out waiting for flushCompleteEvent from " + sensor.getName() +
-                        " waitedFor="+ TIMEOUT_TOLERANCE_US/1000 + "ms");
-            }
-            Log.i(TAG, "testBatchAndFlush PASS " + sensor.getName());
-        } finally {
-            mSensorManager.unregisterListener(listener);
-        }
-    }
-
-    // Wait till the CountDownLatch counts down to zero. If the events are not delivered within
-    // timetoWaitUs, wait an additional maxReportLantencyUs and check if the sensor is streaming
-    // data or not. If the sensor is not streaming at all, fail the test or else wait in increments
-    // of maxReportLantencyUs to collect sensor events.
-    private long waitToCollectAllEvents(long timeToWaitUs, int maxReportLatencyUs,
-            CountDownLatch eventsRemaining)
-            throws InterruptedException {
-        boolean collectedAllEvents = false;
-        long totalTimeWaitedUs = 0;
-        long remainingEvents;
-        final long INCREMENTAL_WAIT_US = maxReportLatencyUs + TimeUnit.SECONDS.toMicros(1);
-        do {
-            totalTimeWaitedUs += timeToWaitUs;
-            remainingEvents = eventsRemaining.getCount();
-            collectedAllEvents = eventsRemaining.await(timeToWaitUs, TimeUnit.MICROSECONDS);
-            timeToWaitUs = INCREMENTAL_WAIT_US;
-        } while (!collectedAllEvents &&
-                (remainingEvents - eventsRemaining.getCount() >=(long)INCREMENTAL_WAIT_US/1000000));
-        return totalTimeWaitedUs;
-    }
-
-    // Call registerListener for multiple sensors at a time and call flush.
+    // TODO: after L release move to SensorBatchingTests and run in all sensors with default
+    //       verifications enabled
     public void testBatchAndFlushWithMutipleSensors() throws Exception {
-        final int MAX_SENSORS = 3;
-        int numSensors = mSensorList.size() < MAX_SENSORS ? mSensorList.size() : MAX_SENSORS;
-        if (numSensors == 0) {
-            return;
+        final int maxSensors = 3;
+        final int maxReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(10);
+        int sensorsCount = mSensorList.size();
+        int numSensors = sensorsCount < maxSensors ? sensorsCount : maxSensors;
+
+        StringBuilder builder = new StringBuilder();
+        ParallelSensorOperation parallelSensorOperation = new ParallelSensorOperation();
+        for (int i = 0; i < sensorsCount && numSensors > 0; ++i) {
+            Sensor sensor = mSensorList.get(i);
+            // skip all non-continuous sensors
+            if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+                TestSensorEnvironment environment = new TestSensorEnvironment(
+                        getContext(),
+                        sensor,
+                        shouldEmulateSensorUnderLoad(),
+                        SensorManager.SENSOR_DELAY_FASTEST,
+                        maxReportLatencyUs);
+                FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+                parallelSensorOperation.add(new TestSensorOperation(environment, executor));
+                --numSensors;
+                builder.append(sensor.getName()).append(", ");
+            }
         }
-        final int numEvents = 500;
-        int rateUs = 0; // DELAY_FASTEST
-        final int maxBatchReportLatencyUs = 10000000;
-        final CountDownLatch eventsRemaining = new CountDownLatch(numSensors * numEvents);
-        final CountDownLatch flushReceived = new CountDownLatch(numSensors);
-        SensorEventListener2 listener = new SensorEventListener2() {
-            @Override
-            public void onSensorChanged(SensorEvent event) {
-                eventsRemaining.countDown();
-            }
 
-            @Override
-            public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            }
-
-            @Override
-            public void onFlushCompleted(Sensor sensor) {
-                flushReceived.countDown();
-            }
-        };
-
-        try {
-            mWakeLock.acquire();
-            StringBuilder registeredSensors = new StringBuilder(30);
-            for (Sensor sensor : mSensorList) {
-                // Skip all non-continuous sensors.
-                if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
-                    continue;
-                }
-                rateUs = Math.max(sensor.getMinDelay(), rateUs);
-                boolean result = mSensorManager.registerListener(listener, sensor,
-                        SensorManager.SENSOR_DELAY_FASTEST, maxBatchReportLatencyUs);
-                assertTrue("registerListener failed for " + sensor.getName(), result);
-                registeredSensors.append(sensor.getName());
-                registeredSensors.append(" ");
-                if (--numSensors == 0) {
-                    break;
-                }
-            }
-            if (registeredSensors.toString().isEmpty()) {
-                return;
-            }
-
-            Log.i(TAG, "testBatchAndFlushWithMutipleSensors " + registeredSensors);
-            long timeToWaitUs =
-                    numEvents*(long)(rateUs/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE) +
-                    maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
-            long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs, maxBatchReportLatencyUs,
-                    eventsRemaining);
-            if (eventsRemaining.getCount() > 0) {
-                failTimedOut(registeredSensors.toString(), (double)totalTimeWaitedUs/1000,
-                        numEvents, (double)rateUs/1000, eventsRemaining.getCount(),
-                        numEvents - eventsRemaining.getCount());
-            }
-            boolean result = mSensorManager.flush(listener);
-            assertTrue("flush failed " + registeredSensors.toString(), result);
-            boolean collectedFlushEvent =
-                    flushReceived.await(TIMEOUT_TOLERANCE_US, TimeUnit.MICROSECONDS);
-            if (!collectedFlushEvent) {
-                fail("Timed out waiting for flushCompleteEvent from " +
-                      registeredSensors.toString() + " waited for=" + timeToWaitUs/1000 + "ms");
-            }
-            Log.i(TAG, "testBatchAndFlushWithMutipleSensors PASS'd");
-        } finally {
-            mSensorManager.unregisterListener(listener);
-            mWakeLock.release();
-        }
+        Log.i(TAG, "Testing batch/flush for sensors: " + builder);
+        parallelSensorOperation.execute();
     }
 
     private void assertSensorValues(Sensor sensor) {
@@ -583,8 +375,8 @@
         assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
                 " " + sensor.getName(), sensor.getResolution() >= 0);
         assertNotNull("Vendor name must not be null " + sensor.getName(), sensor.getVendor());
-        assertTrue("Version must be positive version="  + sensor.getVersion() + " " +
-                    sensor.getName(), sensor.getVersion() > 0);
+        assertTrue("Version must be positive version=" + sensor.getVersion() + " " +
+                sensor.getName(), sensor.getVersion() > 0);
         int fifoMaxEventCount = sensor.getFifoMaxEventCount();
         int fifoReservedEventCount = sensor.getFifoReservedEventCount();
         assertTrue(fifoMaxEventCount >= 0);
@@ -617,19 +409,142 @@
         assertEquals(sensors, mSensorManager.getSensors());
     }
 
-    class TriggerListener extends TriggerEventListener {
-        @Override
-        public void onTrigger(TriggerEvent event) {
+    /**
+     * Verifies that a continuous sensor produces events that have timestamps synchronized with
+     * {@link SystemClock#elapsedRealtimeNanos()}.
+     */
+    private void verifyLongActivation(
+            Sensor sensor,
+            int maxReportLatencyUs,
+            ArrayList<Throwable> errorsFound) throws InterruptedException {
+        if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
+            return;
+        }
+
+        try {
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    getContext(),
+                    sensor,
+                    shouldEmulateSensorUnderLoad(),
+                    SensorManager.SENSOR_DELAY_FASTEST,
+                    maxReportLatencyUs);
+            TestSensorOperation operation =
+                    TestSensorOperation.createOperation(environment, 20, TimeUnit.SECONDS);
+            operation.addVerification(EventGapVerification.getDefault(environment));
+            operation.addVerification(EventOrderingVerification.getDefault(environment));
+            operation.addVerification(
+                    EventTimestampSynchronizationVerification.getDefault(environment));
+
+            Log.i(TAG, "Running timestamp test on: " + sensor.getName());
+            operation.execute();
+        } catch (InterruptedException e) {
+            // propagate so the test can stop
+            throw e;
+        } catch (Throwable e) {
+            errorsFound.add(e);
+            Log.e(TAG, e.getMessage());
         }
     }
 
-    class SensorListener implements SensorEventListener {
-        @Override
-        public void onSensorChanged(SensorEvent event) {
+    /**
+     * Verifies that a client can listen for events, and that
+     * {@link SensorManager#flush(SensorEventListener)} will trigger the appropriate notification
+     * for {@link SensorEventListener2#onFlushCompleted(Sensor)}.
+     */
+    private void verifyRegisterListenerCallFlush(
+            Sensor sensor,
+            Handler handler,
+            ArrayList<Throwable> errorsFound)
+            throws InterruptedException {
+        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+            return;
         }
 
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        try {
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    getContext(),
+                    sensor,
+                    shouldEmulateSensorUnderLoad(),
+                    SensorManager.SENSOR_DELAY_FASTEST,
+                    (int) TimeUnit.SECONDS.toMicros(10));
+            FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+            TestSensorOperation operation = new TestSensorOperation(environment, executor, handler);
+
+            Log.i(TAG, "Running flush test on: " + sensor.getName());
+            operation.execute();
+        } catch (InterruptedException e) {
+            // propagate so the test can stop
+            throw e;
+        } catch (Throwable e) {
+            errorsFound.add(e);
+            Log.e(TAG, e.getMessage());
         }
     }
+
+    private void assertOnErrors(List<Throwable> errorsFound) {
+        if (!errorsFound.isEmpty()) {
+            StringBuilder builder = new StringBuilder();
+            for (Throwable error : errorsFound) {
+                builder.append(error.getMessage()).append("\n");
+            }
+            Assert.fail(builder.toString());
+        }
+    }
+
+    /**
+     * A delegate that drives the execution of Batch/Flush tests.
+     * It performs several operations in order:
+     * - registration
+     * - for continuous sensors it first ensures that the FIFO is filled
+     *      - if events do not arrive on time, an assert will be triggered
+     * - requests flush of sensor data
+     * - waits for {@link SensorEventListener2#onFlushCompleted(Sensor)}
+     *      - if the event does not arrive, an assert will be triggered
+     */
+    private class FlushExecutor implements TestSensorOperation.Executor {
+        private final TestSensorEnvironment mEnvironment;
+        private final int mEventCount;
+
+        public FlushExecutor(TestSensorEnvironment environment, int eventCount) {
+            mEnvironment = environment;
+            mEventCount = eventCount;
+        }
+
+        /**
+         * Consider only continuous mode sensors for testing register listener.
+         *
+         * For on-change sensors, we only use
+         * {@link TestSensorManager#registerListener(TestSensorEventListener)} to associate the
+         * listener with the sensor. So that {@link TestSensorManager#requestFlush()} can be
+         * invoked on it.
+         */
+        @Override
+        public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                throws InterruptedException {
+            int sensorReportingMode = mEnvironment.getSensor().getReportingMode();
+            try {
+                sensorManager.registerListener(listener);
+                if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
+                    listener.waitForEvents(mEventCount);
+                }
+                sensorManager.requestFlush();
+                listener.waitForFlushComplete();
+            } finally {
+                sensorManager.unregisterListener();
+            }
+        }
+    }
+
+    private class NullTriggerEventListener extends TriggerEventListener {
+        @Override
+        public void onTrigger(TriggerEvent event) {}
+    }
+
+    private class NullSensorEventListener implements SensorEventListener {
+        @Override
+        public void onSensorChanged(SensorEvent event) {}
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index 3bd4a03..a98dc59 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -541,7 +541,8 @@
                 sensorType,
                 shouldEmulateSensorUnderLoad(),
                 rateUs);
-        TestSensorOperation op = new TestSensorOperation(environment, 5, TimeUnit.SECONDS);
+        TestSensorOperation op =
+                TestSensorOperation.createOperation(environment, 5, TimeUnit.SECONDS);
         op.addDefaultVerifications();
         op.setLogEvents(true);
         try {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
index ca7d133..3bedc05 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
@@ -17,7 +17,6 @@
 package android.hardware.cts.helpers;
 
 import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -32,21 +31,6 @@
     private final ArrayList<TestSensorEvent> mSensorEventsList = new ArrayList<TestSensorEvent>();
 
     /**
-     * Constructs a {@link CollectingSensorEventListener} with an additional
-     * {@link SensorEventListener2}.
-     */
-    public CollectingSensorEventListener(SensorEventListener2 listener) {
-        super(listener);
-    }
-
-    /**
-     * Constructs a {@link CollectingSensorEventListener}.
-     */
-    public CollectingSensorEventListener() {
-        this(null);
-    }
-
-    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
index b3b8559..0f84ee6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
@@ -51,7 +51,8 @@
      */
     public void execute() throws Throwable {
         CollectingSensorEventListener calibratedTestListener = new CollectingSensorEventListener();
-        CollectingSensorEventListener uncalibratedTestListener = new CollectingSensorEventListener();
+        CollectingSensorEventListener uncalibratedTestListener =
+                new CollectingSensorEventListener();
         mCalibratedSensorManager.registerListener(calibratedTestListener);
         mUncalibratedSensorManager.registerListener(uncalibratedTestListener);
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index a79e5b1..e89b473 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -192,7 +192,7 @@
             TestSensorEnvironment environment,
             String extras) {
         return String.format(
-                "%s | sensor='%s', samplingPeriodUs=%d, maxReportLatencyUs=%d | %s",
+                "%s | sensor='%s', samplingPeriod=%dus, maxReportLatency=%dus | %s",
                 label,
                 environment.getSensor().getName(),
                 environment.getRequestedSamplingPeriodUs(),
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 7be6c0c..6f98e86 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -40,14 +40,15 @@
 public class SensorStats {
     public static final String DELIMITER = "__";
 
-    public static final String FIRST_TIMESTAMP_KEY = "first_timestamp";
-    public static final String LAST_TIMESTAMP_KEY = "last_timestamp";
     public static final String ERROR = "error";
-    public static final String EVENT_COUNT_KEY = "event_count";
     public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
     public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
     public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
     public static final String EVENT_OUT_OF_ORDER_POSITIONS_KEY = "event_out_of_order_positions";
+    public static final String EVENT_TIME_SYNCHRONIZATION_COUNT_KEY =
+            "event_time_synchronization_count";
+    public static final String EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY =
+            "event_time_synchronization_positions";
     public static final String FREQUENCY_KEY = "frequency";
     public static final String JITTER_95_PERCENTILE_PERCENT_KEY = "jitter_95_percentile_percent";
     public static final String MEAN_KEY = "mean";
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 8f33f92..5be30a4 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -28,6 +28,13 @@
  * The environment is self contained and carries its state around all the sensor test framework.
  */
 public class TestSensorEnvironment {
+
+    /**
+     * It represents the fraction of the expected sampling frequency, at which the sensor can
+     * actually produce events.
+     */
+    private static final float MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER = 0.9f;
+
     private final Context mContext;
     private final Sensor mSensor;
     private final boolean mSensorMightHaveMoreListeners;
@@ -40,7 +47,10 @@
      * @param context The context for the test
      * @param sensorType The type of the sensor under test
      * @param samplingPeriodUs The requested collection period for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(Context context, int sensorType, int samplingPeriodUs) {
         this(context, sensorType, false /* sensorMightHaveMoreListeners */, samplingPeriodUs);
     }
@@ -52,7 +62,10 @@
      * @param sensorType The type of the sensor under test
      * @param samplingPeriodUs The requested collection period for the sensor under test
      * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(
             Context context,
             int sensorType,
@@ -72,7 +85,10 @@
      * @param sensorType The type of the sensor under test
      * @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
      * @param samplingPeriodUs The requested collection period for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(
             Context context,
             int sensorType,
@@ -93,7 +109,10 @@
      * @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
      * @param samplingPeriodUs The requested collection period for the sensor under test
      * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+     *
+     * @deprecated Use variants with {@link Sensor} objects.
      */
+    @Deprecated
     public TestSensorEnvironment(
             Context context,
             int sensorType,
@@ -112,6 +131,26 @@
      *
      * @param context The context for the test
      * @param sensor The sensor under test
+     * @param samplingPeriodUs The requested collection period for the sensor under test
+     * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+     */
+    public TestSensorEnvironment(
+            Context context,
+            Sensor sensor,
+            int samplingPeriodUs,
+            int maxReportLatencyUs) {
+        this(context,
+                sensor,
+                false /* sensorMightHaveMoreListeners */,
+                samplingPeriodUs,
+                maxReportLatencyUs);
+    }
+
+    /**
+     * Constructs an environment for sensor testing.
+     *
+     * @param context The context for the test
+     * @param sensor The sensor under test
      * @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load (this
      *                                     usually implies that there are several listeners
      *                                     requesting different sampling periods)
@@ -171,7 +210,8 @@
      * data at different sampling rates (the rates are unknown); false otherwise.
      */
     public boolean isSensorSamplingRateOverloaded() {
-        return mSensorMightHaveMoreListeners && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
+        return mSensorMightHaveMoreListeners
+                && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
     }
 
     /**
@@ -197,6 +237,15 @@
     }
 
     /**
+     * @return The actual sampling period at which a sensor can sample data. This value is a
+     *         fraction of {@link #getExpectedSamplingPeriodUs()}.
+     */
+    public int getMaximumExpectedSamplingPeriodUs() {
+        int expectedSamplingPeriodUs = getExpectedSamplingPeriodUs();
+        return (int) (expectedSamplingPeriodUs / MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER);
+    }
+
+    /**
      * @return The number of axes in the coordinate system of the sensor under test.
      */
     public int getSensorAxesCount() {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 9b3a5e4..ae36e6a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -21,9 +21,12 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener2;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.SystemClock;
 import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -36,13 +39,16 @@
  */
 public class TestSensorEventListener implements SensorEventListener2 {
     public static final String LOG_TAG = "TestSensorEventListener";
-    private static final long EVENT_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
-    private static final long FLUSH_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
+    private static final long EVENT_TIMEOUT_US = TimeUnit.SECONDS.toMicros(5);
+    private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
+
+    private final ArrayList<CountDownLatch> mEventLatches = new ArrayList<CountDownLatch>();
+    private final ArrayList<CountDownLatch> mFlushLatches = new ArrayList<CountDownLatch>();
 
     private final SensorEventListener2 mListener;
+    private final Handler mHandler;
 
-    private volatile CountDownLatch mEventLatch;
-    private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
+    private volatile boolean mEventsReceivedInHandler = true;
     private volatile TestSensorEnvironment mEnvironment;
     private volatile boolean mLogEvents;
 
@@ -50,13 +56,28 @@
      * Construct a {@link TestSensorEventListener}.
      */
     public TestSensorEventListener() {
-        this(null);
+        this(null /* listener */, null /* handler */);
+    }
+
+    /**
+     * Construct a {@link TestSensorEventListener} with a {@link Handler}.
+     */
+    public TestSensorEventListener(Handler handler) {
+        this(null /* listener */, handler);
     }
 
     /**
      * Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}.
      */
     public TestSensorEventListener(SensorEventListener2 listener) {
+        this(listener, null /* handler */);
+    }
+
+    /**
+     * Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}, and it
+     * has a {@link Handler}.
+     */
+    public TestSensorEventListener(SensorEventListener2 listener, Handler handler) {
         if (listener != null) {
             mListener = listener;
         } else {
@@ -67,6 +88,14 @@
                 public void onAccuracyChanged(Sensor sensor, int i) {}
             };
         }
+        mHandler = handler;
+    }
+
+    /**
+     * @return The handler (if any) associated with the instance.
+     */
+    public Handler getHandler() {
+        return mHandler;
     }
 
     /**
@@ -88,6 +117,7 @@
      */
     @Override
     public void onSensorChanged(SensorEvent event) {
+        checkHandler();
         mListener.onSensorChanged(event);
         if (mLogEvents) {
             Log.v(LOG_TAG, String.format(
@@ -98,9 +128,10 @@
                     Arrays.toString(event.values)));
         }
 
-        CountDownLatch eventLatch = mEventLatch;
-        if(eventLatch != null) {
-            eventLatch.countDown();
+        synchronized (mEventLatches) {
+            for (CountDownLatch latch : mEventLatches) {
+                latch.countDown();
+            }
         }
     }
 
@@ -109,6 +140,7 @@
      */
     @Override
     public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        checkHandler();
         mListener.onAccuracyChanged(sensor, accuracy);
     }
 
@@ -117,12 +149,14 @@
      */
     @Override
     public void onFlushCompleted(Sensor sensor) {
-        CountDownLatch latch = mFlushLatch;
-        mFlushLatch = new CountDownLatch(1);
-        if(latch != null) {
-            latch.countDown();
-        }
+        checkHandler();
         mListener.onFlushCompleted(sensor);
+
+        synchronized (mFlushLatches) {
+            for (CountDownLatch latch : mFlushLatches) {
+                latch.countDown();
+            }
+        }
     }
 
     /**
@@ -131,13 +165,23 @@
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
     public void waitForFlushComplete() throws InterruptedException {
-        CountDownLatch latch = mFlushLatch;
-        if(latch == null) {
-            return;
+        CountDownLatch latch = new CountDownLatch(1);
+        synchronized (mFlushLatches) {
+            mFlushLatches.add(latch);
         }
-        Assert.assertTrue(
-                SensorCtsHelper.formatAssertionMessage("WaitForFlush", mEnvironment),
-                latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+
+        try {
+            String message = SensorCtsHelper.formatAssertionMessage(
+                    "WaitForFlush",
+                    mEnvironment,
+                    "timeout=%dus",
+                    FLUSH_TIMEOUT_US);
+            Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+        } finally {
+            synchronized (mFlushLatches) {
+                mFlushLatches.remove(latch);
+            }
+        }
     }
 
     /**
@@ -146,22 +190,34 @@
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
     public void waitForEvents(int eventCount) throws InterruptedException {
-        mEventLatch = new CountDownLatch(eventCount);
+        CountDownLatch eventLatch = new CountDownLatch(eventCount);
+        synchronized (mEventLatches) {
+            mEventLatches.add(eventLatch);
+        }
         try {
-            int rateUs = mEnvironment.getExpectedSamplingPeriodUs();
-            // Timeout is 2 * event count * expected period + batch timeout + default wait
-            long timeoutUs = (2 * eventCount * rateUs)
+            long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
+            // timeout is 2 * event count * expected period + batch timeout + default wait
+            // we multiply by two as not to raise an error in this function even if the events are
+            // streaming at a lower rate than expected, as long as it's not streaming twice as slow
+            // as expected
+            long timeoutUs = (2 * eventCount * samplingPeriodUs)
                     + mEnvironment.getMaxReportLatencyUs()
                     + EVENT_TIMEOUT_US;
-            String message = SensorCtsHelper.formatAssertionMessage(
-                    "WaitForEvents",
-                    mEnvironment,
-                    "requested:%d, received:%d",
-                    eventCount,
-                    eventCount - mEventLatch.getCount());
-            Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
+            boolean success = eventLatch.await(timeoutUs, TimeUnit.MICROSECONDS);
+            if (!success) {
+                String message = SensorCtsHelper.formatAssertionMessage(
+                        "WaitForEvents",
+                        mEnvironment,
+                        "requested=%d, received=%d, timeout=%dus",
+                        eventCount,
+                        eventCount - eventLatch.getCount(),
+                        timeoutUs);
+                Assert.fail(message);
+            }
         } finally {
-            mEventLatch = null;
+            synchronized (mEventLatches) {
+                mEventLatches.remove(eventLatch);
+            }
         }
     }
 
@@ -171,4 +227,22 @@
     public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
         SensorCtsHelper.sleep(duration, timeUnit);
     }
+
+    /**
+     * Asserts that sensor events arrived in the proper thread if a {@link Handler} was associated
+     * with the current instance.
+     *
+     * If no events were received this assertion will be evaluated to {@code true}.
+     */
+    public void assertEventsReceivedInHandler() {
+        Assert.assertTrue(
+                "Events did not arrive in the Looper associated with the given Handler.",
+                mEventsReceivedInHandler);
+    }
+
+    private void checkHandler() {
+        if (mHandler != null) {
+            mEventsReceivedInHandler &= (mHandler.getLooper() == Looper.myLooper());
+        }
+    }
 }
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 dc40ff4..a611bfc 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -19,35 +19,18 @@
 import junit.framework.Assert;
 
 import android.content.Context;
-import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
-import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
 import android.util.Log;
 
-import java.util.concurrent.TimeUnit;
-
 /**
- * A test class that performs the actions of {@link SensorManager} on a single sensor. This
- * class allows for a single sensor to be registered and unregistered as well as performing
- * operations such as flushing the sensor events and gathering events. This class also manages
- * performing the test verifications for the sensor manager.
- * <p>
- * This class requires that operations are performed in the following order:
- * <p><ul>
- * <li>{@link #registerListener(TestSensorEventListener)}</li>
- * <li>{@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
- * <li>{@link #unregisterListener()}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, int)}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, long, TimeUnit)}</li>
- * </ul><p>
- * If methods are called outside of this order, they will print a warning to the log and then
- * return. Both {@link #runSensor(TestSensorEventListener, int)}} and
- * {@link #runSensor(TestSensorEventListener, long, TimeUnit)} will perform the appropriate
- * set up and tear down.
- * <p>
+ * A test class that performs the actions of {@link SensorManager} on a single sensor.
+ * This class allows for a single sensor to be registered and unregistered as well as performing
+ * operations such as flushing the sensor events and gathering events.
+ * This class also manages performing the test verifications for the sensor manager.
+ *
+ * NOTE: this class is expected to mirror {@link SensorManager} operations, and perform the
+ * required test verifications along with them.
  */
 public class TestSensorManager {
     private static final String LOG_TAG = "TestSensorManager";
@@ -55,7 +38,7 @@
     private final SensorManager mSensorManager;
     private final TestSensorEnvironment mEnvironment;
 
-    private TestSensorEventListener mTestSensorEventListener;
+    private volatile TestSensorEventListener mTestSensorEventListener;
 
     /**
      * @deprecated Use {@link #TestSensorManager(TestSensorEnvironment)} instead.
@@ -90,7 +73,7 @@
             return;
         }
 
-        mTestSensorEventListener = listener != null ? listener : new TestSensorEventListener();
+        mTestSensorEventListener = listener;
         mTestSensorEventListener.setEnvironment(mEnvironment);
 
         String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
@@ -98,7 +81,8 @@
                 mTestSensorEventListener,
                 mEnvironment.getSensor(),
                 mEnvironment.getRequestedSamplingPeriodUs(),
-                mEnvironment.getMaxReportLatencyUs());
+                mEnvironment.getMaxReportLatencyUs(),
+                listener.getHandler());
         Assert.assertTrue(message, result);
     }
 
@@ -110,136 +94,24 @@
             Log.w(LOG_TAG, "No listener registered, returning.");
             return;
         }
-
-        mSensorManager.unregisterListener(
-                mTestSensorEventListener,
-                mEnvironment.getSensor());
+        mSensorManager.unregisterListener(mTestSensorEventListener, mEnvironment.getSensor());
+        mTestSensorEventListener.assertEventsReceivedInHandler();
         mTestSensorEventListener = null;
     }
 
     /**
-     * Wait for a specific number of events.
-     */
-    public void waitForEvents(int eventCount) throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            Log.w(LOG_TAG, "No listener registered, returning.");
-            return;
-        }
-        mTestSensorEventListener.waitForEvents(eventCount);
-    }
-
-    /**
-     * Wait for a specific duration.
-     */
-    public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            Log.w(LOG_TAG, "No listener registered, returning.");
-            return;
-        }
-        mTestSensorEventListener.waitForEvents(duration, timeUnit);
-    }
-
-    /**
      * Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
      * the sensor is not registered.
      *
-     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false
+     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} fails.
      */
-    public void startFlush() {
+    public void requestFlush() {
         if (mTestSensorEventListener == null) {
+            Log.w(LOG_TAG, "No listener registered, returning.");
             return;
         }
-
         Assert.assertTrue(
                 SensorCtsHelper.formatAssertionMessage("Flush", mEnvironment),
                 mSensorManager.flush(mTestSensorEventListener));
     }
-
-    /**
-     * Wait for {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will
-     * perform a no-op if the sensor is not registered.
-     *
-     * @throws AssertionError if there is a time out
-     * @throws InterruptedException if the thread was interrupted
-     */
-    public void waitForFlushCompleted() throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            return;
-        }
-        mTestSensorEventListener.waitForFlushComplete();
-    }
-
-    /**
-     * Call {@link SensorManager#flush(SensorEventListener)} and wait for
-     * {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will perform
-     * a no-op if the sensor is not registered.
-     *
-     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false or
-     * if there is a time out
-     * @throws InterruptedException if the thread was interrupted
-     */
-    public void flush() throws InterruptedException {
-        if (mTestSensorEventListener == null) {
-            return;
-        }
-        startFlush();
-        waitForFlushCompleted();
-    }
-
-    /**
-     * Register a listener, wait for a specific number of events, and then unregister the listener.
-     */
-    public void runSensor(TestSensorEventListener listener, int eventCount)
-            throws InterruptedException {
-        if (mTestSensorEventListener != null) {
-            Log.w(LOG_TAG, "Listener already registered, returning.");
-            return;
-        }
-        try {
-            registerListener(listener);
-            waitForEvents(eventCount);
-        } finally {
-            unregisterListener();
-        }
-    }
-
-    /**
-     * Register a listener, wait for a specific duration, and then unregister the listener.
-     */
-    public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit)
-            throws InterruptedException {
-        if (mTestSensorEventListener != null) {
-            Log.w(LOG_TAG, "Listener already registered, returning.");
-            return;
-        }
-        try {
-            registerListener(listener);
-            waitForEvents(duration, timeUnit);
-        } finally {
-            unregisterListener();
-        }
-    }
-
-    /**
-     * 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) throws InterruptedException {
-        if (mTestSensorEventListener != null) {
-            Log.w(LOG_TAG, "Listener already registered, returning.");
-            return;
-        }
-
-        try {
-            registerListener(listener);
-            SensorCtsHelper.sleep(duration, timeUnit);
-            startFlush();
-            listener.waitForFlushComplete();
-        } finally {
-            unregisterListener();
-        }
-    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
index 299f470..7572dc7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
@@ -17,11 +17,11 @@
 package android.hardware.cts.helpers;
 
 import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
 import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+import android.os.Handler;
 
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.LinkedList;
 
 /**
  * A {@link TestSensorEventListener} which performs validations on the received events on the fly.
@@ -30,42 +30,17 @@
  */
 public class ValidatingSensorEventListener extends TestSensorEventListener {
 
-    private final Collection<ISensorVerification> mVerifications =
-            new LinkedList<ISensorVerification>();
-
-    /**
-     * Construct a {@link ValidatingSensorEventListener} with an additional
-     * {@link SensorEventListener2}.
-     */
-    public ValidatingSensorEventListener(SensorEventListener2 listener,
-            ISensorVerification ... verifications) {
-        super(listener);
-        for (ISensorVerification verification : verifications) {
-            mVerifications.add(verification);
-        }
-    }
-
-    /**
-     * Construct a {@link ValidatingSensorEventListener} with an additional
-     * {@link SensorEventListener2}.
-     */
-    public ValidatingSensorEventListener(SensorEventListener2 listener,
-            Collection<ISensorVerification> verifications) {
-        this(listener, verifications.toArray(new ISensorVerification[0]));
-    }
+    private final ArrayList<ISensorVerification> mVerifications =
+            new ArrayList<ISensorVerification>();
 
     /**
      * Construct a {@link ValidatingSensorEventListener}.
      */
-    public ValidatingSensorEventListener(ISensorVerification ... verifications) {
-        this(null, verifications);
-    }
-
-    /**
-     * Construct a {@link ValidatingSensorEventListener}.
-     */
-    public ValidatingSensorEventListener(Collection<ISensorVerification> verifications) {
-        this(null, verifications);
+    public ValidatingSensorEventListener(
+            Collection<ISensorVerification> verifications,
+            Handler handler) {
+        super(handler);
+        mVerifications.addAll(verifications);
     }
 
     /**
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
deleted file mode 100644
index d5aa4b9..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.TestSensorEnvironment;
-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 environment the test environment
-     * @param duration the duration to gather events before calling {@code SensorManager.flush()}
-     * @param timeUnit the time unit of the duration
-     */
-    public TestSensorFlushOperation(
-            TestSensorEnvironment environment,
-            long duration,
-            TimeUnit timeUnit) {
-        super(environment);
-        mDuration = duration;
-        mTimeUnit = timeUnit;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
-        mSensorManager.runSensorAndFlush(listener, mDuration, mTimeUnit);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected VerifiableSensorOperation doClone() {
-        return new TestSensorFlushOperation(mEnvironment, 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 695e1a7..6e53dbb 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
@@ -16,9 +16,27 @@
 
 package android.hardware.cts.helpers.sensoroperations;
 
+import junit.framework.Assert;
+
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 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.EventTimestampSynchronizationVerification;
+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.os.Handler;
 
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -29,70 +47,204 @@
  * events and then run all the tests.
  * </p>
  */
-public class TestSensorOperation extends VerifiableSensorOperation {
-    private final Integer mEventCount;
-    private final Long mDuration;
-    private final TimeUnit mTimeUnit;
+public class TestSensorOperation extends AbstractSensorOperation {
+    private final Collection<ISensorVerification> mVerifications =
+            new HashSet<ISensorVerification>();
+
+    private final TestSensorManager mSensorManager;
+    private final TestSensorEnvironment mEnvironment;
+    private final Executor mExecutor;
+    private final Handler mHandler;
+
+    private boolean mLogEvents;
 
     /**
-     * Create a {@link TestSensorOperation}.
-     *
-     * @param environment the test environment
-     * @param eventCount the number of events to gather
+     * An interface that defines an abstraction for operations to be performed by the
+     * {@link TestSensorOperation}.
      */
-    public TestSensorOperation(TestSensorEnvironment environment, int eventCount) {
-        this(environment, eventCount, null /* duration */, null /* timeUnit */);
+    public interface Executor {
+        void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                throws InterruptedException;
     }
 
     /**
      * Create a {@link TestSensorOperation}.
-     *
-     * @param environment the test environment
-     * @param duration the duration to gather events for
-     * @param timeUnit the time unit of the duration
+     */
+    public TestSensorOperation(TestSensorEnvironment environment, Executor executor) {
+        this(environment, executor, null /* handler */);
+    }
+
+    /**
+     * Create a {@link TestSensorOperation}.
      */
     public TestSensorOperation(
             TestSensorEnvironment environment,
-            long duration,
-            TimeUnit timeUnit) {
-        this(environment, null /* eventCount */, duration, timeUnit);
+            Executor executor,
+            Handler handler) {
+        mEnvironment = environment;
+        mExecutor = executor;
+        mHandler = handler;
+        mSensorManager = new TestSensorManager(mEnvironment);
     }
 
     /**
-     * Private helper constructor.
+     * Set whether to log events.
      */
-    private TestSensorOperation(
+    public void setLogEvents(boolean logEvents) {
+        mLogEvents = logEvents;
+    }
+
+    /**
+     * Set all of the default test expectations.
+     */
+    public void addDefaultVerifications() {
+        addVerification(EventGapVerification.getDefault(mEnvironment));
+        addVerification(EventOrderingVerification.getDefault(mEnvironment));
+        addVerification(FrequencyVerification.getDefault(mEnvironment));
+        addVerification(JitterVerification.getDefault(mEnvironment));
+        addVerification(MagnitudeVerification.getDefault(mEnvironment));
+        addVerification(MeanVerification.getDefault(mEnvironment));
+        addVerification(StandardDeviationVerification.getDefault(mEnvironment));
+        addVerification(EventTimestampSynchronizationVerification.getDefault(mEnvironment));
+    }
+
+    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() throws InterruptedException {
+        getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
+
+        ValidatingSensorEventListener listener =
+                new ValidatingSensorEventListener(mVerifications, mHandler);
+        listener.setLogEvents(mLogEvents);
+
+        mExecutor.execute(mSensorManager, listener);
+
+        boolean failed = false;
+        StringBuilder sb = new StringBuilder();
+        for (ISensorVerification verification : mVerifications) {
+            failed |= evaluateResults(verification, sb);
+        }
+
+        if (failed) {
+            String msg = SensorCtsHelper
+                    .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
+            getStats().addValue(SensorStats.ERROR, msg);
+            Assert.fail(msg);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public TestSensorOperation clone() {
+        TestSensorOperation operation = new TestSensorOperation(mEnvironment, mExecutor);
+        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(mEnvironment, getStats());
+        } catch (AssertionError e) {
+            if (sb.length() > 0) {
+                sb.append(", ");
+            }
+            sb.append(e.getMessage());
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Creates an operation that will wait for a given amount of events to arrive.
+     *
+     * @param environment The test environment.
+     * @param eventCount The number of events to wait for.
+     */
+    public static TestSensorOperation createOperation(
             TestSensorEnvironment environment,
-            Integer eventCount,
-            Long duration,
-            TimeUnit timeUnit) {
-        super(environment);
-        mEventCount = eventCount;
-        mDuration = duration;
-        mTimeUnit = timeUnit;
+            final int eventCount) {
+        Executor executor = new Executor() {
+            @Override
+            public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                    throws InterruptedException {
+                try {
+                    sensorManager.registerListener(listener);
+                    listener.waitForEvents(eventCount);
+                } finally {
+                    sensorManager.unregisterListener();
+                }
+            }
+        };
+        return new TestSensorOperation(environment, executor);
     }
 
     /**
-     * {@inheritDoc}
+     * Creates an operation that will wait for a given amount of time to collect events.
+     *
+     * @param environment The test environment.
+     * @param duration The duration to wait for events.
+     * @param timeUnit The time unit for {@code duration}.
      */
-    @Override
-    protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
-        if (mEventCount != null) {
-            mSensorManager.runSensor(listener, mEventCount);
-        } else {
-            mSensorManager.runSensor(listener, mDuration, mTimeUnit);
-        }
+    public static TestSensorOperation createOperation(
+            TestSensorEnvironment environment,
+            final long duration,
+            final TimeUnit timeUnit) {
+        Executor executor = new Executor() {
+            @Override
+            public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                    throws InterruptedException {
+                try {
+                    sensorManager.registerListener(listener);
+                    listener.waitForEvents(duration, timeUnit);
+                } finally {
+                    sensorManager.unregisterListener();
+                }
+            }
+        };
+        return new TestSensorOperation(environment, executor);
     }
 
     /**
-     * {@inheritDoc}
+     * Creates an operation that will wait for a given amount of time before calling
+     * {@link TestSensorManager#requestFlush()}.
+     *
+     * @param environment The test environment.
+     * @param duration The duration to wait before calling {@link TestSensorManager#requestFlush()}.
+     * @param timeUnit The time unit for {@code duration}.
      */
-    @Override
-    protected VerifiableSensorOperation doClone() {
-        if (mEventCount != null) {
-            return new TestSensorOperation(mEnvironment, mEventCount);
-        } else {
-            return new TestSensorOperation(mEnvironment, mDuration, mTimeUnit);
-        }
+    public static TestSensorOperation createFlushOperation(
+            TestSensorEnvironment environment,
+            final long duration,
+            final TimeUnit timeUnit) {
+        Executor executor = new Executor() {
+            @Override
+            public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                    throws InterruptedException {
+                try {
+                    sensorManager.registerListener(listener);
+                    SensorCtsHelper.sleep(duration, timeUnit);
+                    sensorManager.requestFlush();
+                    listener.waitForFlushComplete();
+                } finally {
+                    sensorManager.unregisterListener();
+                }
+            }
+        };
+        return new TestSensorOperation(environment, executor);
     }
 }
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
deleted file mode 100644
index 57018eb..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import junit.framework.Assert;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorStats;
-import android.hardware.cts.helpers.TestSensorEnvironment;
-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 TestSensorEnvironment mEnvironment;
-
-    private final Collection<ISensorVerification> mVerifications =
-            new HashSet<ISensorVerification>();
-
-    private boolean mLogEvents = false;
-
-    /**
-     * Create a {@link TestSensorOperation}.
-     *
-     * @param environment the test environment
-     */
-    public VerifiableSensorOperation(TestSensorEnvironment environment) {
-        mEnvironment = environment;
-        mSensorManager = new TestSensorManager(mEnvironment);
-    }
-
-    /**
-     * Set whether to log events.
-     */
-    public void setLogEvents(boolean logEvents) {
-        mLogEvents = logEvents;
-    }
-
-    /**
-     * Set all of the default test expectations.
-     */
-    public void addDefaultVerifications() {
-        addVerification(EventGapVerification.getDefault(mEnvironment));
-        addVerification(EventOrderingVerification.getDefault(mEnvironment));
-        addVerification(FrequencyVerification.getDefault(mEnvironment));
-        addVerification(JitterVerification.getDefault(mEnvironment));
-        addVerification(MagnitudeVerification.getDefault(mEnvironment));
-        addVerification(MeanVerification.getDefault(mEnvironment));
-        // Skip SigNumVerification since it has no default
-        addVerification(StandardDeviationVerification.getDefault(mEnvironment));
-    }
-
-    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() throws InterruptedException {
-        getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
-
-        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("VerifySensorOperation", mEnvironment, 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) throws InterruptedException;
-
-    /**
-     * 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(mEnvironment, 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/sensorverification/AbstractSensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
index 911ae3a..acf71bb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -18,6 +18,8 @@
 
 import android.hardware.cts.helpers.TestSensorEvent;
 
+import java.util.List;
+
 /**
  * Abstract class that deals with the synchronization of the sensor verifications.
  */
@@ -52,18 +54,38 @@
      */
     protected abstract void addSensorEventInternal(TestSensorEvent event);
 
+    protected <TEvent extends IndexedEvent> int[] getIndexArray(List<TEvent> indexedEvents) {
+        int eventsCount = indexedEvents.size();
+        int[] indices = new int[eventsCount];
+        for (int i = 0; i < eventsCount; i++) {
+            indices[i] = indexedEvents.get(i).index;
+        }
+        return indices;
+    }
+
+    /**
+     * Helper class to store the index and current event.
+     * Events are added to the verification in the order they are generated, the index represents
+     * the position of the given event, in the list of added events.
+     */
+    protected class IndexedEvent {
+        public final int index;
+        public final TestSensorEvent event;
+
+        public IndexedEvent(int index, TestSensorEvent event) {
+            this.index = index;
+            this.event = event;
+        }
+    }
+
     /**
      * Helper class to store the index, previous event, and current event.
      */
-    protected class IndexedEventPair {
-        public final int index;
-        public final TestSensorEvent event;
+    protected class IndexedEventPair extends IndexedEvent {
         public final TestSensorEvent previousEvent;
 
-        public IndexedEventPair(int index, TestSensorEvent event,
-                TestSensorEvent previousEvent) {
-            this.index = index;
-            this.event = event;
+        public IndexedEventPair(int index, TestSensorEvent event, TestSensorEvent previousEvent) {
+            super(index, event);
             this.previousEvent = previousEvent;
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
index 156afa8..b692f0f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -63,19 +63,14 @@
     public void verify(TestSensorEnvironment environment, SensorStats stats) {
         if (environment.isSensorSamplingRateOverloaded()) {
             // the verification is not reliable on environments under load
-            stats.addValue(PASSED_KEY, true);
+            stats.addValue(PASSED_KEY, "skipped (under load)");
             return;
         }
 
         final int count = mEventGaps.size();
         stats.addValue(PASSED_KEY, count == 0);
         stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
-
-        final int[] indices = new int[count];
-        for (int i = 0; i < indices.length; i++) {
-            indices[i] = mEventGaps.get(i).index;
-        }
-        stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, indices);
+        stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, getIndexArray(mEventGaps));
 
         if (count > 0) {
             StringBuilder sb = new StringBuilder();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
index 6598725..7b77ead 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
@@ -75,12 +75,9 @@
         final int count = mOutOfOrderEvents.size();
         stats.addValue(PASSED_KEY, count == 0);
         stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY, count);
-
-        final int[] indices = new int[count];
-        for (int i = 0; i < indices.length; i++) {
-            indices[i] = mOutOfOrderEvents.get(i).index;
-        }
-        stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY, indices);
+        stats.addValue(
+                SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY,
+                getIndexArray(mOutOfOrderEvents));
 
         if (count > 0) {
             StringBuilder sb = new StringBuilder();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
new file mode 100644
index 0000000..d4a1f38
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import junit.framework.Assert;
+
+import android.hardware.SensorEvent;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the timestamp of the {@link SensorEvent} is
+ * synchronized with {@link SystemClock#elapsedRealtimeNanos()}, based on a given threshold.
+ */
+public class EventTimestampSynchronizationVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "timestamp_synchronization_passed";
+
+    // number of indices to print in assertion message before truncating
+    private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+    private static final long DEFAULT_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(500);
+
+    private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<TestSensorEvent>();
+
+    private final long mMaximumSynchronizationErrorNs;
+    private final long mReportLatencyNs;
+
+    /**
+     * Constructs an instance of {@link EventTimestampSynchronizationVerification}.
+     *
+     * @param maximumSynchronizationErrorNs The valid threshold for timestamp synchronization.
+     * @param reportLatencyNs The latency on which batching events are received
+     */
+    public EventTimestampSynchronizationVerification(
+            long maximumSynchronizationErrorNs,
+            long reportLatencyNs) {
+        mMaximumSynchronizationErrorNs = maximumSynchronizationErrorNs;
+        mReportLatencyNs = reportLatencyNs;
+    }
+
+    /**
+     * Gets a default {@link EventTimestampSynchronizationVerification}.
+     *
+     * @param environment The test environment
+     * @return The verification or null if the verification is not supported in the given
+     *         environment.
+     */
+    public static EventTimestampSynchronizationVerification getDefault(
+            TestSensorEnvironment environment) {
+        int reportLatencyUs = environment.getMaxReportLatencyUs();
+        int fifoMaxEventCount = environment.getSensor().getFifoMaxEventCount();
+        if (fifoMaxEventCount > 0) {
+            int fifoBasedReportLatencyUs =
+                    fifoMaxEventCount * environment.getMaximumExpectedSamplingPeriodUs();
+            reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+
+        }
+        long reportLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
+        return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS, reportLatencyNs);
+    }
+
+    @Override
+    public void verify(TestSensorEnvironment environment, SensorStats stats) {
+        StringBuilder errorMessageBuilder =
+                new StringBuilder(" event timestamp synchronization failures: ");
+        List<IndexedEvent> failures = verifyTimestampSynchronization(errorMessageBuilder);
+
+        int failuresCount = failures.size();
+        stats.addValue(SensorStats.EVENT_TIME_SYNCHRONIZATION_COUNT_KEY, failuresCount);
+        stats.addValue(
+                SensorStats.EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY,
+                getIndexArray(failures));
+
+        boolean success = failures.isEmpty();
+        stats.addValue(PASSED_KEY, success);
+        errorMessageBuilder.insert(0, failuresCount);
+        Assert.assertTrue(errorMessageBuilder.toString(), success);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public EventTimestampSynchronizationVerification clone() {
+        return new EventTimestampSynchronizationVerification(
+                mMaximumSynchronizationErrorNs,
+                mReportLatencyNs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        mCollectedEvents.add(event);
+    }
+
+    /**
+     * Verifies timestamp synchronization for all sensor events.
+     * The verification accounts for a lower and upper threshold, such thresholds are adjusted for
+     * batching cases.
+     *
+     * @param builder A string builder to store error messaged found in the collected sensor events.
+     * @return A list of events tha failed the verification.
+     */
+    private List<IndexedEvent> verifyTimestampSynchronization(StringBuilder builder) {
+        int collectedEventsCount = mCollectedEvents.size();
+        ArrayList<IndexedEvent> failures = new ArrayList<IndexedEvent>();
+
+        for (int i = 0; i < collectedEventsCount; ++i) {
+            TestSensorEvent event = mCollectedEvents.get(i);
+            long eventTimestampNs = event.timestamp;
+            long receivedTimestampNs = event.receivedTimestamp;
+            long upperThresholdNs = receivedTimestampNs;
+            long lowerThresholdNs = receivedTimestampNs - mMaximumSynchronizationErrorNs
+                    - mReportLatencyNs;
+
+            if (eventTimestampNs < lowerThresholdNs || eventTimestampNs > upperThresholdNs) {
+                if (failures.size() < TRUNCATE_MESSAGE_LENGTH) {
+                    builder.append("position=").append(i);
+                    builder.append(", timestamp=").append(eventTimestampNs).append("ns");
+                    builder.append(", expected=[").append(lowerThresholdNs);
+                    builder.append(", ").append(upperThresholdNs).append("]ns; ");
+                }
+                failures.add(new IndexedEvent(i, event));
+            }
+        }
+        if (failures.size() >= TRUNCATE_MESSAGE_LENGTH) {
+            builder.append("more; ");
+        }
+        return failures;
+    }
+}
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
index 3765809..b298b97 100644
--- a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -26,6 +26,7 @@
 import android.location.Criteria;
 import android.location.GpsStatus;
 import android.location.GpsStatus.Listener;
+import android.location.GpsStatus.NmeaListener;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
@@ -227,18 +228,32 @@
     @UiThreadTest
     public void testGpsStatusListener() {
         try {
-            // .addGpsStatusListener returns true if the listener added successfully
-            if (mManager.addGpsStatusListener(new MockGpsStatusListener())) {
-                fail("Should have failed to add a gps status listener");
-            }
+            mManager.addGpsStatusListener(new MockGpsStatusListener());
+            fail("Should have failed to add a gps status listener");
         } catch (SecurityException e) {
             // expected
         }
 
         try {
-            if (mManager.addGpsStatusListener(null)) {
-                fail("Should have failed to add null as a gps status listener");
-            }
+            mManager.addGpsStatusListener(null);
+            fail("Should have failed to add null as a gps status listener");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @UiThreadTest
+    public void testGpsStatusNmeaListener() {
+        try {
+            mManager.addNmeaListener(new MockGpsStatusNmeaListener());
+            fail("Should have failed to add a gps status nmea listener");
+        } catch (SecurityException e) {
+            // expected
+        }
+
+        try {
+            mManager.addNmeaListener(null);
+            fail("Should have failed to add null as a gps status nmea listener");
         } catch (SecurityException e) {
             // expected
         }
@@ -478,4 +493,20 @@
             mHasCallOnGpsStatusChanged = true;
         }
     }
+
+    private static class MockGpsStatusNmeaListener implements NmeaListener {
+        private boolean mHasCallOnNmeaReceived;
+
+        public boolean hasCallOnNmeaReceived() {
+            return mHasCallOnNmeaReceived;
+        }
+
+        public void reset(){
+            mHasCallOnNmeaReceived = false;
+        }
+
+        public void onNmeaReceived(long timestamp, String nmea) {
+            mHasCallOnNmeaReceived = true;
+        }
+    }
 }
diff --git a/tests/tests/media/res/raw/sine1khzm40db.wav b/tests/tests/media/res/raw/sine1khzm40db.wav
new file mode 100644
index 0000000..ba541c4
--- /dev/null
+++ b/tests/tests/media/res/raw/sine1khzm40db.wav
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
new file mode 100644
index 0000000..9515c22
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.Context;
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.audiofx.LoudnessEnhancer;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class LoudnessEnhancerTest extends PostProcTestBase {
+
+    private String TAG = "LoudnessEnhancerTest";
+    private LoudnessEnhancer mLE;
+
+    //-----------------------------------------------------------------
+    // LOUDNESS ENHANCER TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    public void test0_0ConstructorAndRelease() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+        assertNotNull("null AudioManager", am);
+        getLoudnessEnhancer(0);
+        releaseLoudnessEnhancer();
+
+        int session = am.generateAudioSessionId();
+        assertTrue("cannot generate new session", session != AudioManager.ERROR);
+        getLoudnessEnhancer(session);
+        releaseLoudnessEnhancer();
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test set/get target gain
+    public void test1_0TargetGain() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        getLoudnessEnhancer(0);
+        try {
+            mLE.setTargetGain(0);
+            assertEquals("target gain differs from value set", 0, mLE.getTargetGain());
+            mLE.setTargetGain(800);
+            assertEquals("target gain differs from value set", 800, mLE.getTargetGain());
+        } catch (IllegalArgumentException e) {
+            fail("target gain illegal argument");
+        } catch (UnsupportedOperationException e) {
+            fail("target gain unsupported operation");
+        } catch (IllegalStateException e) {
+            fail("target gain operation called in wrong state");
+        } finally {
+            releaseLoudnessEnhancer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+    private void getLoudnessEnhancer(int session) {
+        releaseLoudnessEnhancer();
+        try {
+            mLE = new LoudnessEnhancer(session);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "getLoudnessEnhancer() LoudnessEnhancer not found exception: ", e);
+        } catch (UnsupportedOperationException e) {
+            Log.e(TAG, "getLoudnessEnhancer() Effect library not loaded exception: ", e);
+        }
+        assertNotNull("could not create LoudnessEnhancer", mLE);
+    }
+
+    private void releaseLoudnessEnhancer() {
+        if (mLE != null) {
+            mLE.release();
+            mLE = null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index 8c91e9b..871f602 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -16,10 +16,15 @@
 
 package android.media.cts;
 
+import com.android.cts.media.R;
+
+import android.content.Context;
 import android.media.audiofx.AudioEffect;
 import android.media.AudioFormat;
 import android.media.AudioManager;
+import android.media.MediaPlayer;
 import android.media.audiofx.Visualizer;
+import android.media.audiofx.Visualizer.MeasurementPeakRms;
 import android.os.Looper;
 import android.test.AndroidTestCase;
 import android.util.Log;
@@ -126,7 +131,7 @@
     // 2 - check capture
     //----------------------------------
 
-    //Test case 2.0: test cature in polling mode
+    //Test case 2.0: test capture in polling mode
     public void test2_0PollingCapture() throws Exception {
         if (!hasAudioOutput()) {
             return;
@@ -217,6 +222,113 @@
     }
 
     //-----------------------------------------------------------------
+    // 3 - check measurement mode MEASUREMENT_MODE_NONE
+    //----------------------------------
+
+    //Test case 3.0: test setting NONE measurement mode
+    public void test3_0MeasurementModeNone() throws Exception {
+        try {
+            getVisualizer(0);
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+
+            int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_NONE);
+            assertEquals("setMeasurementMode for NONE doesn't report success",
+                    Visualizer.SUCCESS, status);
+
+            int mode = mVisualizer.getMeasurementMode();
+            assertEquals("getMeasurementMode reports NONE",
+                    Visualizer.MEASUREMENT_MODE_NONE, mode);
+
+        } catch (IllegalStateException e) {
+            fail("method called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            releaseVisualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 4 - check measurement mode MEASUREMENT_MODE_PEAK_RMS
+    //----------------------------------
+
+    //Test case 4.0: test setting peak / RMS measurement mode
+    public void test4_0MeasurementModePeakRms() throws Exception {
+        try {
+            getVisualizer(0);
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+
+            int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+            assertEquals("setMeasurementMode for PEAK_RMS doesn't report success",
+                    Visualizer.SUCCESS, status);
+
+            int mode = mVisualizer.getMeasurementMode();
+            assertEquals("getMeasurementMode doesn't report PEAK_RMS",
+                    Visualizer.MEASUREMENT_MODE_PEAK_RMS, mode);
+
+        } catch (IllegalStateException e) {
+            fail("method called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            releaseVisualizer();
+        }
+    }
+
+    //Test case 4.1: test measurement of peak / RMS
+    public void test4_1MeasurePeakRms() throws Exception {
+        try {
+            // this test will play a 1kHz sine wave with peaks at -40dB
+            MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
+            final int EXPECTED_PEAK_MB = -4015;
+            final int EXPECTED_RMS_MB =  -4300;
+            final int MAX_MEASUREMENT_ERROR_MB = 1500;
+            assertNotNull("null MediaPlayer", mp);
+
+            AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+            assertNotNull("null AudioManager", am);
+            int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+            am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                    am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
+            getVisualizer(mp.getAudioSessionId());
+            mp.setLooping(true);
+            mp.start();
+
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+            int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+            // make sure we're playing long enough so the measurement is valid
+            Thread.sleep(500);
+            assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
+                    Visualizer.SUCCESS, status);
+            MeasurementPeakRms measurement = new MeasurementPeakRms();
+            status = mVisualizer.getMeasurementPeakRms(measurement);
+            assertEquals("getMeasurementPeakRms() reports failure",
+                    Visualizer.SUCCESS, status);
+            Log.i("VisTest", "peak="+measurement.mPeak+"  rms="+measurement.mRms);
+            int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
+            int deltaRms =  Math.abs(measurement.mRms - EXPECTED_RMS_MB);
+            assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
+            assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
+            mp.stop();
+            mp.release();
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
+
+        } catch (IllegalStateException e) {
+            fail("method called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            releaseVisualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
     // private methods
     //----------------------------------
 
diff --git a/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
new file mode 100644
index 0000000..0c648c5
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.permission.cts;
+
+import android.content.ContentValues;
+import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for TV API related permissions.
+ */
+public class TvPermissionTest extends AndroidTestCase {
+    private static final String DUMMY_INPUT_ID = "dummy";
+
+    private boolean mHasTvInputFramework;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHasTvInputFramework = getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private void verifyQuery(Uri uri, final String[] projection,
+            String tableName) throws Exception {
+        try {
+            getContext().getContentResolver().query(uri, projection, null, null, null);
+            fail("Accessing " + tableName + " table should require READ_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyInsert(Uri uri, String tableName) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().insert(uri, values);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyUpdate(Uri uri, String tableName) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().update(uri, values, null, null);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyDelete(Uri uri, String tableName) throws Exception {
+        try {
+            getContext().getContentResolver().delete(uri, null, null);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void testQueryChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQuery(TvContract.Channels.CONTENT_URI, projection, "channels");
+    }
+
+    public void testInsertChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyInsert(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testUpdateChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyUpdate(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testDeleteChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyDelete(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testQueryPrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        final String[] projection = { TvContract.Programs._ID };
+        verifyQuery(TvContract.Programs.CONTENT_URI, projection, "programs");
+    }
+
+    public void testInsertPrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyInsert(TvContract.Programs.CONTENT_URI, "programs");
+    }
+
+    public void testUpdatePrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyUpdate(TvContract.Programs.CONTENT_URI, "programs");
+    }
+
+    public void testDeletePrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyDelete(TvContract.Programs.CONTENT_URI, "programs");
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
new file mode 100644
index 0000000..a2d22d9
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
@@ -0,0 +1,371 @@
+ /*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.renderscript.cts;
+
+import android.renderscript.*;
+
+public class TestVLoad extends RSBaseCompute {
+
+    private ScriptC_vload script;
+    private ScriptC_vload_relaxed scriptRelaxed;
+    Allocation walkAlloc;
+    Allocation inAlloc;
+    Allocation outAlloc;
+    private static java.util.Random random = new java.util.Random();
+
+    final int w = 253;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        random.setSeed(10);
+        script = new ScriptC_vload(mRS);
+        scriptRelaxed = new ScriptC_vload_relaxed(mRS);
+    }
+
+
+
+    protected void createWalk() {
+        int tmp[] = new int[w];
+        boolean b[] = new boolean[w];
+        int toCopy = w;
+        int i = 0;
+
+        while (toCopy > 0) {
+            int x = random.nextInt(w);
+
+            //android.util.Log.v("rs", "x " + x + ", y " + y + ", toCopy " + toCopy);
+            while ((x < w) && b[x]) {
+                x++;
+                if (x >= w) {
+                    x = 0;
+                }
+            }
+
+            int maxsize = 1;
+            b[x] = true;
+            if ((x+1 < w) && !b[x+1]) {
+                maxsize ++;
+                b[x+1] = true;
+                if ((x+2 < w) && !b[x+2]) {
+                    maxsize ++;
+                    b[x+2] = true;
+                    if ((x+3 < w) && !b[x+3]) {
+                        maxsize ++;
+                        b[x+3] = true;
+                    }
+                }
+            }
+
+            toCopy -= maxsize;
+            tmp[i] = x | (maxsize << 16);
+            android.util.Log.v("rs", "x " + x + ", vec " + maxsize);
+            i++;
+        }
+
+        walkAlloc = Allocation.createSized(mRS, Element.I32(mRS), i);
+        walkAlloc.copy1DRangeFrom(0, i, tmp);
+    }
+
+    private void testSetup(Type t) {
+        createWalk();
+
+        inAlloc = Allocation.createTyped(mRS, t);
+        outAlloc = Allocation.createTyped(mRS, t);
+        script.set_gAllocIn(inAlloc);
+        script.set_gAllocOut(outAlloc);
+        scriptRelaxed.set_gAllocIn(inAlloc);
+        scriptRelaxed.set_gAllocOut(outAlloc);
+    }
+
+    private void verify(byte[] a1, byte[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(short[] a1, short[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(int[] a1, int[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(long[] a1, long[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(float[] a1, float[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private void verify(double[] a1, double[] a2, String s) {
+        outAlloc.copyTo(a2);
+        for (int i=0; i < w; i++) {
+            if (a1[i] != a2[i]) {
+                throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+            }
+            a2[i] = 0;
+        }
+        outAlloc.copyFrom(a2);
+    }
+
+    private byte[] randomByteArray(int len) {
+        byte t[] = new byte[len];
+        random.nextBytes(t);
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    private short[] randomShortArray(int len) {
+        short t[] = new short[len];
+        for (int i = 0; i < t.length; i++) {
+            t[i] = (short)(random.nextInt() & 0xffff);
+        }
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    private int[] randomIntArray(int len) {
+        int t[] = new int[len];
+        for (int i = 0; i < t.length; i++) {
+            t[i] = random.nextInt();
+        }
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    private long[] randomLongArray(int len) {
+        long t[] = new long[len];
+        for (int i = 0; i < t.length; i++) {
+            t[i] = random.nextLong();
+        }
+        inAlloc.copyFrom(t);
+        return t;
+    }
+
+    public void testVload_char() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        script.forEach_copy2d_char(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch char: ");
+    }
+
+    public void testVload_uchar() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        script.forEach_copy2d_uchar(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch uchar: ");
+    }
+
+    public void testVload_char_relaxed() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        scriptRelaxed.forEach_copy2d_char(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed char: ");
+    }
+
+    public void testVload_uchar_relaxed() {
+        testSetup(Type.createX(mRS, Element.I8(mRS), w));
+        byte tmp[] = randomByteArray(w);
+        byte tmp2[] = new byte[w];
+        scriptRelaxed.forEach_copy2d_uchar(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed uchar: ");
+    }
+
+    public void testVload_short() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        script.forEach_copy2d_short(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch short: ");
+    }
+
+    public void testVload_ushort() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        script.forEach_copy2d_ushort(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ushort: ");
+    }
+
+    public void testVload_short_relaxed() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        scriptRelaxed.forEach_copy2d_short(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed short: ");
+    }
+
+    public void testVload_ushort_relaxed() {
+        testSetup(Type.createX(mRS, Element.I16(mRS), w));
+        short tmp[] = randomShortArray(w);
+        short tmp2[] = new short[w];
+        scriptRelaxed.forEach_copy2d_ushort(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ushort: ");
+    }
+
+    public void testVload_int() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        script.forEach_copy2d_int(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch int: ");
+    }
+
+    public void testVload_uint() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        script.forEach_copy2d_uint(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch uint: ");
+    }
+
+    public void testVload_int_relaxed() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        scriptRelaxed.forEach_copy2d_int(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed int: ");
+    }
+
+    public void testVload_uint_relaxed() {
+        testSetup(Type.createX(mRS, Element.I32(mRS), w));
+        int tmp[] = randomIntArray(w);
+        int tmp2[] = new int[w];
+        scriptRelaxed.forEach_copy2d_uint(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch uint: ");
+    }
+
+    public void testVload_long() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        script.forEach_copy2d_long(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch long: ");
+    }
+
+    public void testVload_ulong() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        script.forEach_copy2d_ulong(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ulong: ");
+    }
+    public void testVload_long_relaxed() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        scriptRelaxed.forEach_copy2d_long(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed long: ");
+    }
+    public void testVload_ulong_relaxed() {
+        testSetup(Type.createX(mRS, Element.I64(mRS), w));
+        long tmp[] = randomLongArray(w);
+        long tmp2[] = new long[w];
+        scriptRelaxed.forEach_copy2d_ulong(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch ulong: ");
+    }
+
+    public void testVload_float() {
+        testSetup(Type.createX(mRS, Element.F32(mRS), w));
+        float tmp[] = new float[w];
+        float tmp2[] = new float[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextFloat();
+        }
+        inAlloc.copyFrom(tmp);
+        script.forEach_copy2d_float(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch float: ");
+    }
+
+    public void testVload_float_relaxed() {
+        testSetup(Type.createX(mRS, Element.F32(mRS), w));
+        float tmp[] = new float[w];
+        float tmp2[] = new float[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextFloat();
+        }
+        inAlloc.copyFrom(tmp);
+        scriptRelaxed.forEach_copy2d_float(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed float: ");
+    }
+
+    public void testVload_double() {
+        testSetup(Type.createX(mRS, Element.F64(mRS), w));
+        double tmp[] = new double[w];
+        double tmp2[] = new double[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextDouble();
+        }
+        inAlloc.copyFrom(tmp);
+        script.forEach_copy2d_double(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch double: ");
+    }
+
+    public void testVload_double_relaxed() {
+        testSetup(Type.createX(mRS, Element.F64(mRS), w));
+        double tmp[] = new double[w];
+        double tmp2[] = new double[w];
+        for (int i=0; i < w; i++) {
+            tmp[i] = random.nextDouble();
+        }
+        inAlloc.copyFrom(tmp);
+        scriptRelaxed.forEach_copy2d_double(walkAlloc);
+        verify(tmp, tmp2, "Data mismatch relaxed double: ");
+    }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
new file mode 100644
index 0000000..cdf5fd1
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
@@ -0,0 +1,60 @@
+ /*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty)                                                 \
+    void __attribute__((kernel)) copy2d_##ty(int xy_v) {            \
+        int lx = xy_v & 0xff;                                       \
+        int vecsize = (xy_v & 0xff0000) >> 16;                      \
+        switch(vecsize) {                                           \
+        case 1: {                                                   \
+                ty i = rsGetElementAt_##ty(gAllocIn, lx);           \
+                rsSetElementAt_##ty(gAllocOut, i, lx);              \
+            } break;                                                \
+        case 2: {                                                   \
+                ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##2(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 3: {                                                   \
+                ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##3(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 4: {                                                   \
+                ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##4(gAllocOut, i, lx);      \
+            } break;                                                \
+        }                                                           \
+    }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
new file mode 100644
index 0000000..61940ba
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
@@ -0,0 +1,62 @@
+ /*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+#pragma rs_fp_relaxed
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty)                                                 \
+    void __attribute__((kernel)) copy2d_##ty(int xy_v) {            \
+        int lx = xy_v & 0xff;                                       \
+        int vecsize = (xy_v & 0xff0000) >> 16;                      \
+        switch(vecsize) {                                           \
+        case 1: {                                                   \
+                ty i = rsGetElementAt_##ty(gAllocIn, lx);           \
+                rsSetElementAt_##ty(gAllocOut, i, lx);              \
+            } break;                                                \
+        case 2: {                                                   \
+                ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##2(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 3: {                                                   \
+                ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##3(gAllocOut, i, lx);      \
+            } break;                                                \
+        case 4: {                                                   \
+                ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+                rsAllocationVStoreX_##ty##4(gAllocOut, i, lx);      \
+            } break;                                                \
+        }                                                           \
+    }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index fa862c1..46d0868 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -31,7 +31,8 @@
 		android_security_cts_SeccompDeathTestService.cpp \
 		android_security_cts_SELinuxTest.cpp \
 		android_security_cts_MMapExecutableTest.cpp \
-		android_security_cts_NetlinkSocket.cpp
+		android_security_cts_NetlinkSocket.cpp \
+		android_security_cts_AudioPolicyBinderTest.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index 0e91b4e..ca8e841 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -26,6 +26,7 @@
 extern int register_android_security_cts_SeccompDeathTestService(JNIEnv*);
 extern int register_android_security_cts_SELinuxTest(JNIEnv*);
 extern int register_android_security_cts_MMapExecutableTest(JNIEnv* env);
+extern int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env);
 
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env = NULL;
@@ -70,5 +71,9 @@
         return JNI_ERR;
     }
 
+    if (register_android_security_cts_AudioPolicyBinderTest(env)) {
+        return JNI_ERR;
+    }
+
     return JNI_VERSION_1_4;
 }
diff --git a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
new file mode 100644
index 0000000..7e24a27
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioPolicyBinderTest-JNI"
+
+#include <jni.h>
+#include <binder/IServiceManager.h>
+#include <media/IAudioPolicyService.h>
+#include <media/AudioSystem.h>
+#include <system/audio.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+using namespace android;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
+ */
+
+static bool init(sp<IAudioPolicyService>& aps, audio_io_handle_t *output, int *session)
+{
+    aps = 0;
+    if (output != NULL) {
+        *output = AUDIO_IO_HANDLE_NONE;
+    }
+    if (session != NULL) {
+        *session = AUDIO_UNIQUE_ID_ALLOCATE;
+    }
+
+    int64_t startTime = 0;
+    sp<IServiceManager> sm = defaultServiceManager();
+    while (aps == 0) {
+        sp<IBinder> binder = defaultServiceManager()->checkService(String16("media.audio_policy"));
+        if (binder == 0) {
+            if (startTime == 0) {
+                startTime = uptimeMillis();
+            } else if ((uptimeMillis()-startTime) > 10000) {
+                ALOGE("timeout while getting audio policy service");
+                return false;
+            }
+            sleep(1);
+        } else {
+            aps = interface_cast<IAudioPolicyService>(binder);
+        }
+    }
+
+    if (output != NULL) {
+        // get a valid output. Any use case will do.
+        for (int stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT; stream++) {
+            *output = AudioSystem::getOutput((audio_stream_type_t)stream);
+            if (*output != AUDIO_IO_HANDLE_NONE) {
+                break;
+            }
+        }
+        if (*output == AUDIO_IO_HANDLE_NONE) {
+            ALOGE("cannot get valid audio output");
+            return false;
+        }
+    }
+    if (session != NULL) {
+        //get a valid session
+        *session = AudioSystem::newAudioUniqueId();
+        if (*session == AUDIO_UNIQUE_ID_ALLOCATE) {
+            ALOGE("cannot get valid audio session");
+            return false;
+        }
+    }
+    return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::startOutput() cannot be called with an
+ * invalid stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_startOutput(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioPolicyService> aps;
+    audio_io_handle_t output;
+    int session;
+
+    if (!init(aps, &output, &session)) {
+        return false;
+    }
+
+    status_t status = aps->startOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1), session);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    status = aps->startOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::stopOutput() cannot be called with an
+ * invalid stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_stopOutput(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioPolicyService> aps;
+    audio_io_handle_t output;
+    int session;
+
+    if (!init(aps, &output, &session)) {
+        return false;
+    }
+
+    status_t status = aps->stopOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1), session);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    status = aps->stopOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::isStreamActive() cannot be called with an
+ * invalid stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_isStreamActive(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioPolicyService> aps;
+
+    if (!init(aps, NULL, NULL)) {
+        return false;
+    }
+
+    status_t status = aps->isStreamActive((audio_stream_type_t)(AUDIO_STREAM_MIN -1), 0);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    status = aps->isStreamActive((audio_stream_type_t)AUDIO_STREAM_CNT, 0);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+ * invalid stream type.
+ * Test with NUM_RANDOM_TESTS random values for stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_isStreamActiveRemotely(JNIEnv* env __unused,
+                                                           jobject thiz __unused)
+{
+    sp<IAudioPolicyService> aps;
+
+    if (!init(aps, NULL, NULL)) {
+        return false;
+    }
+
+    status_t status = aps->isStreamActiveRemotely((audio_stream_type_t)(AUDIO_STREAM_MIN -1), 0);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    status = aps->isStreamActiveRemotely((audio_stream_type_t)AUDIO_STREAM_CNT, 0);
+    if (status == NO_ERROR) {
+        return false;
+    }
+    return true;
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "native_test_startOutput", "()Z",
+            (void *) android_security_cts_AudioPolicy_test_startOutput },
+    {  "native_test_stopOutput", "()Z",
+                (void *) android_security_cts_AudioPolicy_test_stopOutput },
+    {  "native_test_isStreamActive", "()Z",
+                (void *) android_security_cts_AudioPolicy_test_isStreamActive },
+    {  "native_test_isStreamActiveRemotely", "()Z",
+                (void *) android_security_cts_AudioPolicy_test_isStreamActiveRemotely },
+};
+
+int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/security/cts/AudioPolicyBinderTest");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
new file mode 100644
index 0000000..b307247
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.security.cts;
+
+import junit.framework.TestCase;
+
+public class AudioPolicyBinderTest extends TestCase {
+
+    static {
+        System.loadLibrary("ctssecurity_jni");
+    }
+
+    /**
+     * Checks that IAudioPolicyService::startOutput() cannot be called with an
+     * invalid stream type.
+     */
+    public void test_startOutput() throws Exception {
+        assertTrue(native_test_startOutput());
+    }
+
+    /**
+     * Checks that IAudioPolicyService::stopOutput() cannot be called with an
+     * invalid stream type.
+     */
+    public void test_stopOutput() throws Exception {
+        assertTrue(native_test_stopOutput());
+    }
+
+    /**
+     * Checks that IAudioPolicyService::isStreamActive() cannot be called with an
+     * invalid stream type.
+     */
+    public void test_isStreamActive() throws Exception {
+        assertTrue(native_test_isStreamActive());
+    }
+
+    /**
+     * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+     * invalid stream type.
+     */
+    public void test_isStreamActiveRemotely() throws Exception {
+        assertTrue(native_test_isStreamActiveRemotely());
+    }
+
+    private static native boolean native_test_startOutput();
+    private static native boolean native_test_stopOutput();
+    private static native boolean native_test_isStreamActive();
+    private static native boolean native_test_isStreamActiveRemotely();
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index 651a199..857e489 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -395,4 +395,111 @@
         verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0,
                 channelId, channelUri);
     }
+
+    private void verifyQueryWithSortOrder(Uri uri, final String[] projection,
+            String sortOrder) throws Exception {
+        try {
+            getContext().getContentResolver().query(uri, projection, null, null, sortOrder);
+            fail("Setting sortOrder should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    private void verifyQueryWithSelection(Uri uri, final String[] projection,
+            String selection) throws Exception {
+        try {
+            getContext().getContentResolver().query(uri, projection, selection, null, null);
+            fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    private void verifyUpdateWithSelection(Uri uri, String selection) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().update(uri, values, selection, null);
+            fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    private void verifyDeleteWithSelection(Uri uri, String selection) throws Exception {
+        try {
+            getContext().getContentResolver().delete(uri, selection, null);
+            fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void testAllEpgPermissionBlocksSortOrderOnQuery_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQueryWithSortOrder(TvContract.Channels.CONTENT_URI, projection,
+                TvContract.Channels._ID + " ASC");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnQuery_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQueryWithSelection(TvContract.Channels.CONTENT_URI, projection,
+                TvContract.Channels._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnUpdate_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyUpdateWithSelection(TvContract.Channels.CONTENT_URI,
+                TvContract.Channels._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnDelete_Channels() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyDeleteWithSelection(TvContract.Channels.CONTENT_URI,
+                TvContract.Channels._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSortOrderOnQuery_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Programs._ID };
+        verifyQueryWithSortOrder(TvContract.Programs.CONTENT_URI, projection,
+                TvContract.Programs._ID + " ASC");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnQuery_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        final String[] projection = { TvContract.Channels._ID };
+        verifyQueryWithSelection(TvContract.Programs.CONTENT_URI, projection,
+                TvContract.Programs._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnUpdate_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyUpdateWithSelection(TvContract.Programs.CONTENT_URI,
+                TvContract.Programs._ID + ">0");
+    }
+
+    public void testAllEpgPermissionBlocksSelectionOnDelete_Programs() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        verifyDeleteWithSelection(TvContract.Programs.CONTENT_URI,
+                TvContract.Programs._ID + ">0");
+    }
 }
diff --git a/tests/tests/view/res/anim-land/changing_reset_state_anim.xml b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
new file mode 100644
index 0000000..0b3279a
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_state_list_animator.xml b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
new file mode 100644
index 0000000..4c1c41e
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+    </item>
+    <!-- base state-->
+    <item>
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="200dp" android:valueType="floatType"/>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_test_animator.xml b/tests/tests/view/res/anim-land/changing_test_animator.xml
new file mode 100644
index 0000000..d5c83cd
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:propertyName="x" android:duration="100"
+                android:interpolator="@android:anim/bounce_interpolator"
+                android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_reset_state_anim.xml b/tests/tests/view/res/anim/changing_reset_state_anim.xml
new file mode 100644
index 0000000..dd2b233
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_state_list_animator.xml b/tests/tests/view/res/anim/changing_state_list_animator.xml
new file mode 100644
index 0000000..6bcc0d9
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+    </item>
+    <!-- base state-->
+    <item>
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_test_animator.xml b/tests/tests/view/res/anim/changing_test_animator.xml
new file mode 100644
index 0000000..739a71b
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:propertyName="x" android:duration="100"
+                android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+                android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/reset_state_anim.xml b/tests/tests/view/res/anim/reset_state_anim.xml
new file mode 100644
index 0000000..4bbbe62
--- /dev/null
+++ b/tests/tests/view/res/anim/reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_animator.xml b/tests/tests/view/res/anim/test_animator.xml
new file mode 100644
index 0000000..94e9ec8
--- /dev/null
+++ b/tests/tests/view/res/anim/test_animator.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="@dimen/test_animator_target_x" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="@dimen/test_animator_target_y" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="left" android:duration="100" android:valueTo="2" android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator.xml b/tests/tests/view/res/anim/test_state_list_animator.xml
new file mode 100644
index 0000000..b6a4822
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <set>
+            <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+        </set>
+    </item>
+    <item android:state_enabled="true" android:state_pressed="false">
+        <set>
+            <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+        </set>
+    </item>
+    <!-- base state-->
+    <item android:animation="@anim/reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator_2.xml b/tests/tests/view/res/anim/test_state_list_animator_2.xml
new file mode 100644
index 0000000..6aeb41f
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator_2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+    </item>
+    <!-- base state-->
+    <item android:animation="@anim/changing_reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/values-land/dimens.xml b/tests/tests/view/res/values-land/dimens.xml
new file mode 100644
index 0000000..17b5395
--- /dev/null
+++ b/tests/tests/view/res/values-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <dimen name="test_animator_target_y">30dp</dimen>
+    <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced
+    in the XML on purpose-->
+    <dimen name="reset_state_value">100dp</dimen>
+    <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+    <dimen name="changing_state_list_anim_target_x_value">200dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/res/values/dimens.xml b/tests/tests/view/res/values/dimens.xml
new file mode 100644
index 0000000..16e5084
--- /dev/null
+++ b/tests/tests/view/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <dimen name="test_animator_target_x">10dp</dimen>
+    <dimen name="test_animator_target_y">20dp</dimen>
+    <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced in the XML on purpose-->
+    <dimen name="reset_state_value">50dp</dimen>
+    <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+    <dimen name="changing_state_list_anim_target_x_value">100dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
new file mode 100644
index 0000000..eeda5a3
--- /dev/null
+++ b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
@@ -0,0 +1,271 @@
+/*
+* 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.view.animation.cts;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.StateListAnimator;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Debug;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.cts.view.R;
+
+public class AnimatorInflaterTest
+        extends ActivityInstrumentationTestCase2<AnimationTestCtsActivity> {
+
+    Set<Integer> identityHashes = new HashSet<Integer>();
+
+    public AnimatorInflaterTest() {
+        super("com.android.cts.view", AnimationTestCtsActivity.class);
+    }
+
+    private void assertUnique(Object object) {
+        assertUnique(object, "");
+    }
+
+    private void assertUnique(Object object, String msg) {
+        final int code = System.identityHashCode(object);
+        assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
+
+    }
+
+    public void testLoadAnimatorWithDifferentInterpolators() throws Throwable {
+        Animator anim1 = AnimatorInflater
+                .loadAnimator(getActivity(), R.anim.changing_test_animator);
+        rotate();
+        Animator anim2 = AnimatorInflater
+                .loadAnimator(getActivity(), R.anim.changing_test_animator);
+        assertNotSame(anim1, anim2);
+        assertNotSame("interpolater is orientation dependent, should change",
+                anim1.getInterpolator(), anim2.getInterpolator());
+    }
+
+    /**
+     * Tests animators with dimension references.
+     */
+    public void testLoadAnimator() throws Throwable {
+        // to identify objects
+        Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        assertNotSame("a different animation should be returned", anim1, anim2);
+        assertSame("interpolator should be shallow cloned", anim1.getInterpolator(),
+                anim2.getInterpolator());
+        for (int i = 0; i < 2; i++) {
+            float targetX = getActivity().getResources()
+                    .getDimension(R.dimen.test_animator_target_x);
+            // y value changes in landscape orientation
+            float targetY = getActivity().getResources()
+                    .getDimension(R.dimen.test_animator_target_y);
+            for (Animator anim : new Animator[]{anim1, anim2}) {
+                assertTrue(anim instanceof AnimatorSet);
+                assertUnique(anim);
+                AnimatorSet set = (AnimatorSet) anim;
+                assertEquals("should have 3 sub animations", 3, set.getChildAnimations().size());
+                for (Animator subAnim : set.getChildAnimations()) {
+                    assertUnique(subAnim);
+                    assertTrue(subAnim instanceof ObjectAnimator);
+                }
+                final ObjectAnimator child1 = (ObjectAnimator) set.getChildAnimations().get(0);
+                final ObjectAnimator child2 = (ObjectAnimator) set.getChildAnimations().get(1);
+                final DummyObject dummyObject = new DummyObject();
+                runTestOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        for (ObjectAnimator animator : new ObjectAnimator[]{child1, child2}) {
+                            animator.setTarget(dummyObject);
+                            animator.setupStartValues();
+                            animator.start();
+                            animator.end();
+                        }
+                    }
+                });
+                assertEquals(targetX, dummyObject.x);
+                assertEquals(targetY, dummyObject.y);
+            }
+            if (i == 0) {
+                rotate();
+            }
+            anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+            anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+
+        }
+    }
+
+    private void rotate() throws Throwable {
+        final Activity activity = getActivity();
+        int orientation = activity.getResources().getConfiguration().orientation;
+        final int nextOrientation = orientation == Configuration.ORIENTATION_LANDSCAPE
+                ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+                : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                activity.getClass().getName(), null, false);
+        getInstrumentation().addMonitor(monitor);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.setRequestedOrientation(nextOrientation);
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+        Activity newAct = getInstrumentation()
+                .waitForMonitorWithTimeout(monitor, TimeUnit.SECONDS.toMillis(2));
+        if (newAct != null) {
+            setActivity(newAct);
+        }
+    }
+
+    /**
+     * Simple state list animator test that checks for cloning
+     */
+    public void testLoadStateListAnimator() {
+        StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.test_state_list_animator);
+        StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.test_state_list_animator);
+        assertUnique(sla1);
+        assertUnique(sla2);
+    }
+
+    /**
+     * Tests a state list animator which has an @anim reference that has different xmls per
+     * orientation
+     */
+    public void testLoadStateListAnimatorWithChangingResetState() throws Throwable {
+        loadStateListAnimatorWithChangingResetStateTest();
+        rotate();
+        loadStateListAnimatorWithChangingResetStateTest();
+    }
+
+    private void loadStateListAnimatorWithChangingResetStateTest() throws Throwable {
+        final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.test_state_list_animator_2);
+        final View testView = getTestView();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testView.setStateListAnimator(sla);
+                testView.jumpDrawablesToCurrentState();
+            }
+        });
+        float resetValue = getActivity().getResources().getDimension(R.dimen.reset_state_value);
+        getInstrumentation().waitForIdleSync();
+        assertEquals(resetValue, testView.getX());
+        assertEquals(resetValue, testView.getY());
+        assertEquals(resetValue, testView.getZ());
+    }
+
+    /**
+     * Tests a state list animator which has different xml descriptions per orientation.
+     */
+    public void testLoadChangingStateListAnimator() throws Throwable {
+        loadChangingStateListAnimatorTest();
+        rotate();
+        loadChangingStateListAnimatorTest();
+    }
+
+    private void loadChangingStateListAnimatorTest() throws Throwable {
+        final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+                R.anim.changing_state_list_animator);
+        final View testView = getTestView();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testView.setStateListAnimator(sla);
+                testView.jumpDrawablesToCurrentState();
+            }
+        });
+        float targetValue = getActivity().getResources()
+                .getDimension(R.dimen.changing_state_list_anim_target_x_value);
+        getInstrumentation().waitForIdleSync();
+        assertEquals(targetValue, testView.getX());
+    }
+
+    /**
+     * Tests that makes sure that reloaded animator is not affected by previous changes
+     */
+    public void testReloadedAnimatorIsNotModified() throws Throwable {
+        final Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        final CountDownLatch mStarted = new CountDownLatch(1);
+        final AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                mStarted.countDown();
+            }
+        };
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                anim1.setTarget(getTestView());
+                anim1.addListener(listener);
+                anim1.start();
+            }
+        });
+        Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+        assertTrue(anim1.isStarted());
+        assertFalse(anim2.isStarted());
+        assertFalse("anim2 should not include the listener",
+                anim2.getListeners() != null && anim2.getListeners().contains(listener));
+        assertTrue("animator should start", mStarted.await(10, TimeUnit.SECONDS));
+        assertFalse(anim2.isRunning());
+
+    }
+
+    public View getTestView() {
+        return getActivity().findViewById(R.id.anim_window);
+    }
+
+    class DummyObject {
+
+        float x;
+        float y;
+
+        public float getX() {
+            return x;
+        }
+
+        public void setX(float x) {
+            this.x = x;
+        }
+
+        public float getY() {
+            return y;
+        }
+
+        public void setY(float y) {
+            this.y = y;
+        }
+    }
+}
+